source: subversion/applications/utils/gary68/boundaries.pl @ 15268

Last change on this file since 15268 was 15268, checked in by gary68, 10 years ago

-

File size: 35.4 KB
Line 
1#
2
3#
4# boundaries.pl by gary68
5#
6#
7#
8# Copyright (C) 2009, Gerhard Schwanz
9#
10# 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
11# Free Software Foundation; either version 3 of the License, or (at your option) any later version.
12#
13# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
15#
16# You should have received a copy of the GNU General Public License along with this program; if not, see <http://www.gnu.org/licenses/>
17#
18#
19#
20# IN:  file.osm
21#
22# OUT: file.htm (list)
23# OUT: file.csv (list)
24# OUT: file.hirarchy.htm (list)
25# OUT: file.hirarchy.csv (list)
26# OUT: fileXXXXX.poly (borders original)
27# OUT: fileSimplifiedXXXXX.poly (borders simplified)
28# OUT: fileXXXXX.png (map of border)
29#
30# relation member roles used: outer and none
31#
32# parameters and options see below
33#
34# Version 2
35# - find hirarchies and report
36# - use simplified polygons for hirarchy if specified
37# - time calc
38# - error handling for relations caontaining themselves as member
39# - check for max nesting level when parsing relation members being relations (prevent loops eating memory and terminate program)
40# - resize option (-resize, -factor (float > 1.0)
41# - pics with resized polygons
42#
43#
44# TODO
45# - command line error handling
46#
47
48use strict ;
49use warnings ;
50
51use File::stat ;
52use Time::localtime ; 
53use Getopt::Long ;
54use Math::Polygon ;
55
56use OSM::osm 4.4 ;
57use OSM::osmgraph ;
58
59my $program = "boundaries.pl" ;
60my $usage = $program . " see code GetOptions" ;
61my $version = "2.0 BETA (005)" ;
62my $maxNestingLevel = 10 ; # for relations
63
64my $nodeId ;            # variables for reading nodes
65my $nodeUser ;
66my $nodeLat ;
67my $nodeLon ;
68my @nodeTags ;
69my $aRef1 ;
70my $aRef2 ;
71
72my $wayId ;             # variables for reading ways
73my $wayUser ;
74my @wayNodes ;
75my @wayTags ;
76
77my $relationId ;        # variables for reading relations
78my $relationUser ;
79my @relationMembers ;
80my @relationTags ;
81
82my %lon = () ; my %lat = () ;   # all node positions
83
84my @neededNodes = () ;  # will be used to load only needed data
85my @neededWays = () ;   # will be used to load only needed data
86
87my %wayNodesHash = () ; # nodes forming a way
88my %relationWays = () ; # ways contained (first directly, later also indirect by relation reference) in relation
89my %relationRelations = () ;    # relations contained in relation (referenced)
90my %validRelation = () ;        # can be used for evaluation
91my %completeWay = () ;          # this is the boundary as built by checkSegments, all nodes in correct order
92my %relationName = () ;         # relation data
93my %relationType = () ;         # relation data
94my %relationBoundary = () ;     # relation data
95my %relationLength = () ;       # relation data, original
96my %relationAdminLevel = () ;   # relation data
97my %relationPolygon = () ;              # relation original polygon
98my %relationPolygonSimplified = () ;    # relation simplified polygon
99my %relationPolygonResized = () ;       # relation resized polygon
100my %relationIsIn = () ;         # lists boundaries this relation is inside
101my %relationSize = () ;         # area as returned by math::polygon->area (no projection applied, so no real value! used only to sort is_ins)
102
103my $relationCount = 0 ;         # total
104my $wayCount = 0 ; 
105my $invalidWayCount = 0 ; 
106my %invalidWays ;               # node count < 2, osmcut, osmosis, error...
107my $adminInvalidCount = 0 ;     # how many relations are not used due to admin restriction
108
109# command line things
110my $optResult ;
111my $verbose = "" ;
112my $adminLevelOpt = "" ;
113my $polyOpt = "" ;
114my $hirarchyOpt = 0 ;
115my $simplifyOpt = "" ;
116my $debugOpt = "" ;
117my $picOpt = "" ;
118my $picSize = 1024 ; # default pic size longitude in pixels
119my $resizeOpt = "" ;
120my $resizeFactor = 1.05 ; # 5% bigger default
121my $osmName = "" ; 
122my $htmlName = "" ; my $htmlFile ;
123my $csvName = "" ; my $csvFile ;
124my $polyBaseName = "" ;
125my $polyName = "" ; my $polyFile ;
126
127# defaults for simplify
128my $simplifySlope = 0.001 ; # IN DEGREES, ~100m
129my $simplifySame = 0.002 ; # IN DEGREES, ~200m
130my $simplifyNpk = 2 ;    # max nodes per kilometer for simplified polygon
131
132
133$optResult = GetOptions (       "in=s"          => \$osmName,           # the in file, mandatory
134                                "html=s"        => \$htmlName,          # output file html, mandatory ([dir/]*.htm)
135                                "csv=s"         => \$csvName,           # output file csv, mandatory ([dir/]*.csv)
136                                "poly"          => \$polyOpt,           # option to create poly files, then give polyBaseName
137                                "polybase:s"    => \$polyBaseName,      # base filename for poly files. relId is appended. also used for pic names. [dir/]name
138                                "simplify"      => \$simplifyOpt,       # should simplified polygons be used?
139                                "slope:f"       => \$simplifySlope,     # simplify (Math::Polygon). distance in DEGREES. With three points X(n),X(n+1),X(n+2), the point X(n+1) will be removed if the length of the path over all three points is less than slope longer than the direct path between X(n) and X(n+2)
140                                "same:f"        => \$simplifySame,      # distance (IN DEGREES) for nodes to be considered the same
141                                "npk:f"         => \$simplifyNpk,       # max nodes per km when simplifying
142                                "debug"         => \$debugOpt,         
143                                "pics"          => \$picOpt,            # specifies if pictures of polygons are drawn. polybasename must be given.
144                                "hirarchy"      => \$hirarchyOpt,       # specifies if hirarchies of boundaries are calculated. don't together use with adminlevel. can/should be used with -simplify, then simplified polygons are used for building the hirarchy - much faster
145                                "resize"        => \$resizeOpt, # specifies if new resized polygon will be produced (-polygon must be specified, maybe use -factor, if -simplify is given, simplified polygon will be resized)
146                                "factor:f"      => \$resizeFactor,      # specifies how much bigger the resized polygon will be
147                                "picsize:i"     => \$picSize,           # specifies pic size longitude in pixels
148                                "adminlevel:s"  => \$adminLevelOpt,     # specifies which boundaries to look at
149                                "verbose"       => \$verbose) ;         # turns twitter on
150
151
152
153my $time0 = time() ;
154my $time1 ;
155
156print "\n$program $version \nfor file $osmName\n\n" ;
157
158#if ($optResult == 0) {
159#       die ("usage...\n") ;
160#}
161
162#
163# PARSING RELATIONS
164# after this step rudimentary data of relations is present.
165# however relation members are not yet evaluated
166#
167print "parsing relations...\n" ;
168openOsmFile ($osmName) ;
169print "- skipping nodes...\n" ;
170skipNodes() ;
171print "- skipping ways...\n" ;
172skipWays() ;
173print "- checking...\n" ;
174
175($relationId, $relationUser, $aRef1, $aRef2) = getRelation () ;
176if ($relationId != -1) {
177        @relationMembers = @$aRef1 ;
178        @relationTags = @$aRef2 ;
179}
180
181while ($relationId != -1) {
182
183        my $name = "" ; 
184        my $type = "" ; 
185        my $boundary = "" ; 
186        my $landArea = "" ; 
187        my $adminLevel = "" ;
188
189        my $i ;
190        # process tags
191        if (scalar (@relationTags) > 0) {
192                for ($i=0; $i<scalar (@relationTags); $i++) {
193                        if ( ${$relationTags[$i]}[0] eq "name") { $name =  ${$relationTags[$i]}[1] ; }
194                        if ( ${$relationTags[$i]}[0] eq "type") { $type =  ${$relationTags[$i]}[1] ; }
195                        if ( ${$relationTags[$i]}[0] eq "boundary") { $boundary =  ${$relationTags[$i]}[1] ; }
196                        if ( ${$relationTags[$i]}[0] eq "admin_level") { $adminLevel =  ${$relationTags[$i]}[1] ; }
197                        if ( ${$relationTags[$i]}[0] eq "land_area") { $landArea =  ${$relationTags[$i]}[1] ; }
198                }
199        }
200        # process interesting tags. evaluate relation at all?   
201        my $eval = 0 ;
202        if (  ( ($boundary ne "") or ($landArea ne "") or ($adminLevel ne "") ) and 
203                ( ($type eq "multipolygon") or ($type eq "boundary")  )  ) { 
204                $eval = 1 ; 
205        }
206        # process members if relation is needed and has members
207        if ( ($eval == 1) and (scalar (@relationMembers) > 0) ) {
208                $relationName{$relationId} = $name ;
209                $relationType{$relationId} = $type ;
210                $relationBoundary{$relationId} = $boundary ;
211                $relationAdminLevel{$relationId} = $adminLevel ;
212                @{$relationWays{$relationId}} = () ;
213                @{$relationRelations{$relationId}} = () ;
214                @{$relationIsIn{$relationId}} = () ;
215                if ($verbose) { print "\nfound relation id=$relationId\nname=$name\ntype=$type\nboundary=$boundary\nadminLevel=$adminLevel\nlandArea=$landArea\n" ; }
216                for ($i=0; $i<scalar (@relationMembers); $i++) {
217                        # way?
218                        if ( (${$relationMembers[$i]}[0] eq "way") and ((${$relationMembers[$i]}[2] eq "none") or (${$relationMembers[$i]}[2] eq "outer")) ){ 
219                                push @neededWays, ${$relationMembers[$i]}[1] ; 
220                                push @{$relationWays{$relationId}}, ${$relationMembers[$i]}[1] ; 
221                        }
222                        # relation?
223                        if ( (${$relationMembers[$i]}[0] eq "relation") and ((${$relationMembers[$i]}[2] eq "none") or (${$relationMembers[$i]}[2] eq "outer")) ){ 
224                                if (${$relationMembers[$i]}[1] == $relationId) {
225                                        print "ERROR: relation $relationId contains itself as a member. entry discarded.\n" ;
226                                }
227                                else {
228                                        push @{$relationRelations{$relationId}}, ${$relationMembers[$i]}[1]  ;
229                                }
230                        }
231                }
232        }
233
234        #next relation
235        ($relationId, $relationUser, $aRef1, $aRef2) = getRelation () ;
236        if ($relationId != -1) {
237                @relationMembers = @$aRef1 ;
238                @relationTags = @$aRef2 ;
239        }
240}
241
242closeOsmFile () ;
243
244#
245# GET MORE WAYS out of referenced relations (recursive)
246#
247my $rel ;
248foreach $rel (keys %relationWays) {
249        if (scalar (@{$relationRelations{$rel}}) > 1) {
250                if ($verbose) { print "get relations for relation $rel\n" ; }
251                my (@newWays) = getWays ($rel, 0, @{$relationRelations{$rel}}) ;
252                push @{$relationWays{$rel}}, @newWays ;
253        }
254}
255# now %relationWays contain all needed (recursive) ways of a boundary
256
257#
258# PARSE WAYS FOR NODES
259#
260print "\nparsing ways...\n" ;
261openOsmFile ($osmName) ;
262print "- skipping nodes...\n" ;
263skipNodes() ;
264print "- reading ways...\n" ;
265
266@neededWays = sort { $a <=> $b } @neededWays ;
267
268($wayId, $wayUser, $aRef1, $aRef2) = getWay () ;
269if ($wayId != -1) {
270        @wayNodes = @$aRef1 ;
271        @wayTags = @$aRef2 ;
272}
273while ($wayId != -1) { 
274        my $needed = 0 ;
275        $needed = binSearch ($wayId, \@neededWays ) ;
276        if (scalar (@wayNodes) >= 2) {
277                if ($needed >= 0) {
278                        $wayCount++ ;
279                        @{$wayNodesHash{$wayId}} = @wayNodes ;
280                        push @neededNodes, @wayNodes ;
281                        $invalidWays{$wayId} = 0 ;
282                }
283        }
284        else {
285                # an invalid way itself is no problem first. it will lead to a gap in a boundary if used however...
286                #if ($verbose) { print "ERROR: invalid way (one node only): ", $wayId, "\n" ; }
287                $invalidWayCount++ ;
288                $invalidWays{$wayId} = 1 ;
289        }
290
291        # next way
292        ($wayId, $wayUser, $aRef1, $aRef2) = getWay () ;
293        if ($wayId != -1) {
294                @wayNodes = @$aRef1 ;
295                @wayTags = @$aRef2 ;
296        }
297}
298
299closeOsmFile () ;
300
301if ($verbose) { print "\nthere are $invalidWayCount invalid ways\n\n" ; }
302
303#
304# PARSE NODES FOR POSITIONS
305#
306print "\nparsing nodes...\n" ;
307openOsmFile ($osmName) ;
308
309@neededNodes = sort { $a <=> $b } @neededNodes ;
310
311($nodeId, $nodeLon, $nodeLat, $nodeUser, $aRef1) = getNode () ;
312if ($nodeId != -1) {
313        @nodeTags = @$aRef1 ;
314}
315
316while ($nodeId != -1) {
317        my $needed = 0 ;
318
319        $needed = binSearch ($nodeId, \@neededNodes ) ;
320        if ($needed >= 0) { 
321                $lon{$nodeId} = $nodeLon ; 
322                $lat{$nodeId} = $nodeLat ;
323        }
324
325        # next
326        ($nodeId, $nodeLon, $nodeLat, $nodeUser, $aRef1) = getNode () ;
327        if ($nodeId != -1) {
328                @nodeTags = @$aRef1 ;
329        }
330}
331print "done.\n" ;
332
333#
334# STATS
335#
336print "\n", scalar (keys %relationName), " relations read into memory.\n" ;
337print scalar (keys %wayNodesHash), " ways read into memory.\n" ;
338print scalar (keys %lon), " nodes read into memory.\n\n" ;
339
340#
341# CHECK FOR VALID BOUNDARIES
342#
343print "check for valid boundaries...\n" ;
344my $valid = 0 ; my $invalid = 0 ; 
345foreach $rel (keys %relationWays) {
346
347        my $way ;
348        my $waysValid = 1 ;
349
350        # if the relation ain't got a single way...
351        if (scalar (@{$relationWays{$rel}}) == 0)  { 
352                $waysValid = 0 ; 
353                print "INVALID relation $rel due to no ways\n" ; 
354        }
355
356        # if the boundary contains an invalid way. chances for success are low :-)
357        foreach $way (@{$relationWays{$rel}}) {
358                if ($invalidWays{$way} == 1) { 
359                        $waysValid = 0 ; 
360                        print "INVALID RELATION id=$rel, name=$relationName{$rel} due to invalid way $way\n" ; 
361                }
362        }
363
364        # check for multiple usage of ways. checkSegments doesn't like that.
365        my (@temp) = sort (@{$relationWays{$rel}}) ; my $i ;
366        for ($i=0; $i<$#temp; $i++) {
367                if ($temp[$i] == $temp[$i+1]) {
368                        print "ERROR RELATION id=$rel name=$relationName{$rel} contains way $temp[$i] twice\n" ;
369                        $waysValid = 0 ;
370                } 
371        }
372
373        # if we do have ways...
374        if (scalar @{$relationWays{$rel}} > 0) {
375                my $segments = 0 ; my $open = 0 ; my @way = () ; 
376                if ($waysValid == 1) {
377                        if ($verbose) { print "call checksegments rel = $rel --- ways = @{$relationWays{$rel}}\n" ; } 
378                        # now let's see if we can build a single closed way out of all these ways...
379                        ($segments, $open, @way) = checkSegments3 ( @{$relationWays{$rel}} ) ; 
380                }
381                if ( ($segments == 1) and ($open == 0) and ($waysValid == 1) ) {
382                        $valid ++ ;
383                        $validRelation {$rel} = 1 ;
384                        @{$completeWay{$rel}} = @way ;
385                        if ($verbose) { print "complete and closed way found for relation $rel, name=$relationName{$rel}\n" ; }
386                }
387                else {
388                        $invalid ++ ;
389                        $validRelation {$rel} = 0 ;
390                        print "INVALID RELATION id=$rel, name=$relationName{$rel}, segments=$segments, open=$open, waysValid=$waysValid\n" ;
391                }
392        }
393        else {
394                $invalid ++ ;
395                $validRelation {$rel} = 0 ;
396                print "INVALID RELATION id=$rel, no ways given.\n" ;
397        }
398
399        # check for admin level given as option
400        if (($adminLevelOpt ne "") and ($adminLevelOpt ne $relationAdminLevel{$rel})) {
401                $validRelation {$rel} = 0 ; # invalidate but keep for reference!
402                $adminInvalidCount++ ;
403        }
404}
405print "done.\n" ;
406print "\nTOTAL $valid valid relations, $invalid invalid relations.\n" ;
407print "$adminInvalidCount relations invalidated due to admin level selection.\n" ;
408print "REMAINING for evaluation: ", $valid - $adminInvalidCount, " relations\n\n" ;
409
410#
411# CHECK IF NEEDED NODES COULD BE FOUND
412#
413print "checking if all needed nodes could be found in osm file...\n" ;
414my $nodesMissing = 0 ; my $node ;
415foreach $node (@neededNodes) {
416        if ( (! (defined ($lon{$node}))) or (!(defined ($lat{$node}))) ) {
417                print "ERROR: lon/lat for node $node missing. node not found or not valid in osm file.\n" ;
418                $nodesMissing = 1 ; my $way ;
419                foreach $way (keys %wayNodesHash) {
420                        my $n2 ;
421                        foreach $n2 (@{$wayNodesHash{$way}}) {
422                                if ($node == $n2) {
423                                        print "       node used in way $way\n" ;
424                                }
425                        }
426                }
427        }
428}
429
430#
431# CHECK IF NEEDED WAYS COULD BE FOUND
432#
433print "checking if all needed ways could be found in osm file...\n" ;
434my $waysMissing = 0 ; my $way ;
435foreach $way (@neededWays) {
436        if ( ! (defined ( @{$wayNodesHash{$way}} ) ) ) {
437                if ($invalidWays{$way}) {
438                        print "WARNING way $way invalid in osm file.\n" ;
439                }
440                else {
441                        print "ERROR: nodes for way $way missing. way not found in osm file.\n" ;
442                        $waysMissing = 1 ;
443                        foreach $rel (keys %relationName) {
444                                my $w2 ;
445                                foreach $w2 (@{$relationWays{$rel}}) {
446                                        if ($way == $w2) {
447                                                print "       way used in relation $rel (directly or indirectly).\n" ;
448                                        }
449                                }
450                        }
451                }
452        }
453}
454
455if ($nodesMissing == 1 ) { 
456        print "ERROR: at least one needed node missing in osm file.\n" ; 
457}
458else {
459        print "all needed nodes found.\n" ;
460}
461if ($waysMissing == 1 ) { 
462        print "ERROR: at least one needed way missing in osm file.\n" ; 
463}
464else {
465        print "all needed ways found.\n" ;
466}
467if ( ($nodesMissing == 1) or ($waysMissing == 1) )  {
468        die ("ERROR: at least one needed node or way missing.\n")
469}
470print "done (node and way check).\n" ;
471
472#
473# CALC LENGTH OF VALID RELATIONS,
474# CALC SIMPLIFIED AND RESIZED WAY IF NEEDED
475#
476print "calc length, build polygons, (simplify, resize)...\n" ; 
477foreach $rel (keys %relationWays) {
478        if ($validRelation{$rel}) {
479                my (@wayNodes) = @{$completeWay{$rel}} ;
480                my $length = 0 ;
481                my $i ;
482                for ($i = 0; $i<$#wayNodes; $i++) {
483                        $length += distance ($lon{$wayNodes[$i]}, $lat{$wayNodes[$i]}, $lon{$wayNodes[$i+1]}, $lat{$wayNodes[$i+1]}) ;
484                }
485                $relationLength{$rel} = int ($length * 100) / 100 ;
486
487                if ($polyOpt eq "1" ) {
488                        my @poly = () ; my $node ;
489                        foreach $node (@wayNodes) {
490                                push (@poly, [$lon{$node}, $lat{$node}]) ;
491                        }
492                        $relationPolygon{$rel} = Math::Polygon->new(@poly) ;
493                        $relationSize{$rel} = $relationPolygon{$rel}->area ;
494
495                        if ($simplifyOpt eq "1") { 
496                                my ($maxNodes) = int ($relationLength{$rel} * $simplifyNpk ) ; 
497                                $relationPolygonSimplified{$rel} = $relationPolygon{$rel}->simplify (max_points => $maxNodes, same => $simplifySame, slope => $simplifySlope ) ;
498                                if ($verbose) { print "simplify $rel: nodes=", scalar(@wayNodes), " maxNodes=$maxNodes length=$relationLength{$rel}" ; } 
499                                if ($verbose) { print " new node count=", $relationPolygonSimplified{$rel}->nrPoints, "" ; } 
500                                my ($percent) = int ($relationPolygonSimplified{$rel}->nrPoints / scalar(@wayNodes) * 100 ) ;
501                                if ($verbose) { print " new size of polygon=", $percent, "%\n" ; } 
502                        }
503                        if ($resizeOpt eq "1") { 
504                                if ($simplifyOpt eq "1") { 
505                                        my ($x, $y) = center( $relationPolygonSimplified{$rel} ) ;
506                                        $relationPolygonResized{$rel} = $relationPolygonSimplified{$rel}->resize (center => [$x, $y], scale => $resizeFactor) ;
507                                }
508                                else {
509                                        my ($x, $y) = center( $relationPolygon{$rel} ) ;
510                                        $relationPolygonResized{$rel} = $relationPolygon{$rel}->resize (center => [$x, $y], scale => $resizeFactor) ;
511                                }
512                        }
513                }
514        }
515        else {
516                $relationLength{$rel} = 0 ;
517        }
518}
519print "done.\n" ; 
520
521#
522# WRITE POLY FILES IF SPECIFIED
523#
524if ( ($polyBaseName ne "") and ($polyOpt eq "1") ) {
525        print "write poly files...\n" ; 
526        foreach $rel (keys %relationWays) {
527                if ($validRelation{$rel}) {
528                        my @way ; my $polyFileName = "" ; my @points = () ; my $text = "" ;
529                        if ($verbose) { print "write poly file for relation $rel $relationName{$rel} (", scalar (@points) , " nodes) ...\n" ; }
530
531
532                        if ($simplifyOpt eq "1") { 
533                                $polyFileName = $polyBaseName . ".simplified." . $rel . ".poly" ;
534                                @points = $relationPolygonSimplified{$rel}->points ;
535                                $text = " (SIMPLIFIED)" ;
536                                open ($polyFile, ">", $polyFileName) or die ("can't open poly output file") ;
537                                print $polyFile $relationName{$rel}, $text, "\n" ; # name
538                                print $polyFile "1\n" ;
539                                foreach my $pt ( @points ) {
540                                        printf $polyFile "   %E   %E\n", $pt->[0], $pt->[1] ;
541                                }
542                                print $polyFile "END\n" ;
543                                print $polyFile "END\n" ;
544                                close ($polyFile) ;
545                        }
546
547                        if ($resizeOpt eq "1") { 
548                                $polyFileName = $polyBaseName . ".resized." . $rel . ".poly" ;
549                                @points = $relationPolygonResized{$rel}->points ;
550                                $text = " (RESIZED)" ;
551                                open ($polyFile, ">", $polyFileName) or die ("can't open poly output file") ;
552                                print $polyFile $relationName{$rel}, $text, "\n" ; # name
553                                print $polyFile "1\n" ;
554                                foreach my $pt ( @points ) {
555                                        printf $polyFile "   %E   %E\n", $pt->[0], $pt->[1] ;
556                                }
557                                print $polyFile "END\n" ;
558                                print $polyFile "END\n" ;
559                                close ($polyFile) ;
560                        }
561
562                        $polyFileName = $polyBaseName . "." . $rel . ".poly" ;
563                        @points = $relationPolygon{$rel}->points ;
564
565                        open ($polyFile, ">", $polyFileName) or die ("can't open poly output file") ;
566                        print $polyFile $relationName{$rel}, "\n" ; # name
567                        print $polyFile "1\n" ;
568                        foreach my $pt ( @points ) {
569                                printf $polyFile "   %E   %E\n", $pt->[0], $pt->[1] ;
570                        }
571                        print $polyFile "END\n" ;
572                        print $polyFile "END\n" ;
573                        close ($polyFile) ;
574                }
575        }
576        print "done.\n" ; 
577}
578
579#
580# WRITE PICS IF SPECIFIED
581#
582if ( ($polyBaseName ne "") and ($picOpt eq "1") ) {
583        print "write picture files...\n" ; 
584        foreach $rel (keys %relationWays) {
585                if ($validRelation{$rel}) {
586                        drawPic ($rel) ;
587                }
588        }
589        print "done.\n" ; 
590}
591#
592# BUILD AND PRINT HIRARCHIES
593#
594if ($hirarchyOpt eq "1") {
595        print "building hirarchies...\n" ;
596        my $rel ; my $rel1 ; my $rel2 ; 
597        foreach $rel1 (keys %relationName) {
598                foreach $rel2 (keys %relationName) {
599                        if ( ($rel1 < $rel2) and ($validRelation{$rel1}) and ($validRelation{$rel2}) ) {
600                                my $res ;
601                                if ($simplifyOpt eq "1") {
602                                        $res = isIn ($relationPolygonSimplified{$rel1}, $relationPolygonSimplified{$rel2}) ;
603                                }
604                                else {
605                                        if ($debugOpt eq "1") { print "call isIn $rel1 $rel2\n" ; }
606                                        $res = isIn ($relationPolygon{$rel1}, $relationPolygon{$rel2}) ;
607                                }
608                                if ($res == 2) { push @{$relationIsIn{$rel2}}, $rel1 ; }
609                                if ($res == 1) { push @{$relationIsIn{$rel1}}, $rel2 ; }
610                        }
611                }
612        }
613
614        my ($csvNameHirarchy) = $csvName ;
615        my ($htmlNameHirarchy) = $htmlName ;
616        $csvNameHirarchy =~  s/.csv/.hirarchy.csv/ ;
617        $htmlNameHirarchy =~ s/.htm/.hirarchy.htm/ ;
618
619        open ($htmlFile, ">", $htmlNameHirarchy) or die ("can't open html output file") ;
620        open ($csvFile, ">", $csvNameHirarchy) or die ("can't open csv output file") ;
621
622        printHTMLHeader ($htmlFile, "boundaries by gary68 - hirarchy") ;
623        print $csvFile "Line;RelationId;Name;Type;Boundary;AdminLevel;is_in\n" ;
624        print $htmlFile "<h1>boundary.pl by gary68 - hirarchy</h1>" ;
625        print $htmlFile "<p>Version ", $version, "</p>\n" ;
626        print $htmlFile "<H2>Statistics</H2>\n" ;
627        print $htmlFile "<p>", stringFileInfo ($osmName), "</p>\n" ;
628        print $htmlFile "<h2>Data</h2>" ;
629        printHTMLTableHead ($htmlFile) ;
630        printHTMLTableHeadings ($htmlFile, ("Line", "RelationId", "Name", "Type", "Boundary", "AdminLevel", "is_in")) ;
631
632        my $line = 0 ;
633        # TODO optimize loop!
634        foreach $rel (keys %relationName) {
635                if ( $validRelation{$rel} ) {
636                #if (($validRelation{$rel}) and (scalar (@{$relationIsIn{$rel}}) > 0 ) ) {
637                       
638                        my @is_in = () ;
639                        foreach my $r2 ( @{$relationIsIn{$rel}} ) {
640                                push @is_in, [ $r2, $relationSize{$r2} ] ;
641                        }
642                        @is_in = sort { $a->[1] <=> $b->[1] } (@is_in) ;
643                       
644                        $line++ ;
645                        print $csvFile $line, ";" ;
646                        print $csvFile $rel, ";" ;
647                        print $csvFile $relationName{$rel}, ";" ;
648                        print $csvFile $relationType{$rel}, ";" ;
649                        print $csvFile $relationBoundary{$rel}, ";" ;
650                        print $csvFile $relationAdminLevel{$rel}, ";" ;
651
652                        foreach my $r2 (@is_in) {
653                                print $csvFile $r2->[0], ";" ;
654                        }
655                        print $csvFile "\n" ;
656
657                        printHTMLRowStart ($htmlFile) ;
658                        printHTMLCellRight ($htmlFile, $line) ;
659                        printHTMLCellRight ($htmlFile, historyLink ("relation", $rel) . "(osm) " . analyzerLink($rel) . "(analyzer)" ) ;
660                        printHTMLCellLeft ($htmlFile, $relationName{$rel}) ;
661                        printHTMLCellLeft ($htmlFile, $relationType{$rel}) ;
662                        printHTMLCellLeft ($htmlFile, $relationBoundary{$rel}) ;
663                        printHTMLCellLeft ($htmlFile, $relationAdminLevel{$rel}) ;
664
665                        print $htmlFile "<td align=\"left\">\n" ;
666                        foreach my $r2 (@is_in) {
667                                print $htmlFile historyLink ("relation", $r2->[0]), "(osm) ", analyzerLink ($r2->[0]), "(analyzer) " ;
668                                print $htmlFile $relationName{$r2->[0]}, "<br>\n" ;
669                        }
670                        print $htmlFile "</td>\n" ;
671
672                        printHTMLRowEnd ($htmlFile) ;
673
674                }
675        }
676
677        printHTMLTableFoot ($htmlFile) ;
678        printHTMLFoot ($htmlFile) ;
679
680        close ($htmlFile) ;
681        close ($csvFile) ;
682        print "done.\n" ;
683} # hirarchy
684
685
686
687
688#
689# WRITE OVERVIEW FILES, HTML and CSV
690#
691open ($htmlFile, ">", $htmlName) or die ("can't open html output file") ;
692open ($csvFile, ">", $csvName) or die ("can't open csv output file") ;
693
694printHTMLHeader ($htmlFile, "boundaries by gary68") ;
695print $csvFile "Line;RelationId;Name;Type;Boundary;AdminLevel;Length;Nodes;NodesPerKm\n" ;
696print $htmlFile "<h1>boundary.pl by gary68</h1>" ;
697print $htmlFile "<p>Version ", $version, "</p>\n" ;
698print $htmlFile "<H2>Statistics</H2>\n" ;
699print $htmlFile "<p>", stringFileInfo ($osmName), "</p>\n" ;
700print $htmlFile "<h2>Data</h2>" ;
701printHTMLTableHead ($htmlFile) ;
702printHTMLTableHeadings ($htmlFile, ("Line", "RelationId", "Name", "Type", "Boundary", "AdminLevel", "Length", "Nodes", "NodesPerKm")) ;
703
704my $line = 0 ;
705foreach $rel (keys %relationWays) {
706        if ($validRelation{$rel}) {
707                $line++ ;
708                my $nodesPerKm = int ( scalar ( @{$completeWay{$rel}} / $relationLength{$rel} * 100 ) ) / 100 ;
709                print $csvFile $line, ";" ;
710                print $csvFile $rel, ";" ;
711                print $csvFile "\"", $relationName{$rel}, "\";" ;
712                print $csvFile $relationType{$rel}, ";" ;
713                print $csvFile $relationBoundary{$rel}, ";" ;
714                print $csvFile $relationAdminLevel{$rel}, ";" ;
715                print $csvFile $relationLength{$rel}, ";" ;
716                print $csvFile scalar ( @{$completeWay{$rel}} ), ";" ;
717                print $csvFile $nodesPerKm, "\n" ;
718
719                printHTMLRowStart ($htmlFile) ;
720                printHTMLCellRight ($htmlFile, $line) ;
721                printHTMLCellRight ($htmlFile, historyLink("relation", $rel) . "(osm) " .analyzerLink($rel) . "(analyzer)" ) ;
722                printHTMLCellLeft ($htmlFile, $relationName{$rel}) ;
723                printHTMLCellLeft ($htmlFile, $relationType{$rel}) ;
724                printHTMLCellLeft ($htmlFile, $relationBoundary{$rel}) ;
725                printHTMLCellRight ($htmlFile, $relationAdminLevel{$rel}) ;
726                printHTMLCellRight ($htmlFile, $relationLength{$rel}) ;
727                printHTMLCellRight ($htmlFile, scalar ( @{$completeWay{$rel}} ) ) ;
728                printHTMLCellRight ($htmlFile, $nodesPerKm) ;
729                printHTMLRowEnd ($htmlFile) ;
730
731        }
732}
733printHTMLTableFoot ($htmlFile) ;
734
735print $htmlFile "<h2>Invalid Relations</h2>\n" ;
736printHTMLTableHead ($htmlFile) ;
737printHTMLTableHeadings ($htmlFile, ("RelationId")) ;
738foreach $rel (keys %relationWays) {
739        if (! $validRelation{$rel}) {
740                printHTMLRowStart ($htmlFile) ;
741                printHTMLCellRight ($htmlFile, historyLink("relation", $rel) . "(osm) " .analyzerLink($rel) . "(analyzer)" ) ;
742                printHTMLRowEnd ($htmlFile) ;
743        }
744}
745printHTMLTableFoot ($htmlFile) ;
746
747printHTMLFoot ($htmlFile) ;
748close ($htmlFile) ;
749close ($csvFile) ;
750
751#print "\n$program finished.\n\n";
752print "\n", $program, " ", $osmName, " FINISHED after ", stringTimeSpent (time - $time0), "\n\n" ;
753
754
755
756
757
758
759
760sub checkSegments3 {
761        # sub builds segments for given set of ways.
762        # returns number of segments, number of open segments and complete way if one closed segment was found.
763        my (@ways) = @_ ;
764        my $way ; my $node ;
765        my @openEnds = () ;
766        my $segments = 0 ; my $openSegments = 0 ;
767        my $found = 1 ;
768        my $way1 ; my $way2 ;
769        my $endNodeWay2 ;       my $startNodeWay2 ;
770        my %starts = () ; my %ends = () ;
771        my %wayStart = () ; my %wayEnd = () ;
772        my %wayNodes = () ;
773        my @completeWay = () ;
774
775        #init
776        foreach $way (@ways) {
777                push @{$starts{$wayNodesHash{$way}[0]}}, $way ;
778                push @{$ends{$wayNodesHash{$way}[-1]}}, $way ;
779                $wayStart{$way} = $wayNodesHash{$way}[0] ;
780                $wayEnd{$way} = $wayNodesHash{$way}[-1] ;
781                @{$wayNodes{$way}} = @{$wayNodesHash{$way}} ; # complete...
782                #print "    cs way = $way --- nodes = @{$wayNodesHash{$way}}\n" ;
783        }
784
785        while ($found == 1) {
786                $found = 0 ;
787
788                # check start/start
789                loop1:
790                foreach $node (keys %starts) {
791
792                        # if node with more than 1 connecting way...
793                        if (scalar (@{$starts{$node}}) > 1) {
794                                $way1 = ${$starts{$node}}[0] ; $way2 = ${$starts{$node}}[1] ;
795                                #print "merge start/start $way1 and $way2 at node $node\n" ;
796
797                                # complete
798                                @{$wayNodes{$way1}} = ( reverse ( @{$wayNodes{$way2}}[1..$#{$wayNodes{$way2}}] ), @{$wayNodes{$way1}} ) ;
799
800                                $endNodeWay2 = $wayEnd{$way2} ;
801                                #print "end node way2 = $endNodeWay2\n" ;
802
803                                # way1 gets new start: end way2
804                                push @{$starts{ $endNodeWay2 }}, $way1 ;
805                                $wayStart{$way1} = $endNodeWay2 ;
806
807                                # remove end way2
808                                if (scalar (@{$ends{$endNodeWay2}}) == 1) {
809                                        delete $ends{$endNodeWay2} ;
810                                        #print "$endNodeWay2 removed from end hash\n" ;
811                                }
812                                else {
813                                        @{$ends{$endNodeWay2}} = removeElement ($way2, @{$ends{$endNodeWay2}}) ;
814                                        #print "way $way2 removed from node $endNodeWay2 from end hash\n" ;
815                                }
816                               
817                                # remove way2
818                                delete $wayEnd{$way2} ;
819                                delete $wayStart{$way2} ;
820                                delete $wayNodes{$way2} ;
821
822                                # remove connecting starts
823                                if (scalar @{$starts{$node}} == 2) {
824                                        delete $starts{$node} ;
825                                        #print "$node removed from start hash\n" ;
826                                }
827                                else {
828                                        @{$starts{$node}} = @{$starts{$node}}[2..$#{$starts{$node}}] ;
829                                        #print "first two elements removed from start hash node = $node\n" ;
830                                }
831                                #print "\n" ;
832                                $found = 1 ; 
833                                last loop1 ;
834                        }
835                }
836
837                # check end/end
838                if (!$found) {
839                        loop2:
840                        foreach $node (keys %ends) {
841
842                                # if node with more than 1 connecting way...
843                                if (scalar @{$ends{$node}} > 1) {
844                                        $way1 = ${$ends{$node}}[0] ; $way2 = ${$ends{$node}}[1] ;
845                                        #print "merge end/end $way1 and $way2 at node $node\n" ;
846       
847                                        # complete
848                                        @{$wayNodes{$way1}} = ( @{$wayNodes{$way1}}, reverse ( @{$wayNodes{$way2}}[0..$#{$wayNodes{$way2}}-1] ) )  ;
849
850                                        $startNodeWay2 = $wayStart{$way2} ;
851                                        #print "start node way2 = $startNodeWay2\n" ;
852       
853                                        # way1 gets new end: start way2
854                                        push @{$ends{ $startNodeWay2 }}, $way1 ;
855                                        $wayEnd{$way1} = $startNodeWay2 ;
856       
857                                        # remove start way2
858                                        if (scalar (@{$starts{$startNodeWay2}}) == 1) {
859                                                delete $starts{$startNodeWay2} ;
860                                                #print "$startNodeWay2 removed from start hash\n" ;
861                                        }
862                                        else {
863                                                @{$starts{$startNodeWay2}} = removeElement ($way2, @{$starts{$startNodeWay2}}) ;
864                                                #print "way $way2 removed from node $startNodeWay2 from start hash\n" ;
865                                        }
866                               
867                                        # remove way2
868                                        delete $wayEnd{$way2} ;
869                                        delete $wayStart{$way2} ;
870                                        delete $wayNodes{$way2} ;
871
872                                        # remove connecting ends
873                                        if (scalar @{$ends{$node}} == 2) {
874                                                delete $ends{$node} ;
875                                                #print "$node removed from end hash\n" ;
876                                        }
877                                        else {
878                                                @{$ends{$node}} = @{$ends{$node}}[2..$#{$ends{$node}}] ;
879                                                #print "first two elements removed from end hash node = $node\n" ;
880                                        }
881                                        #print "\n" ;
882                                        $found = 1 ; 
883                                        last loop2 ;
884                                }
885                        }
886                }
887
888
889                # check start/end
890                if (!$found) {
891                        my $wayFound = 0 ;
892                        loop3:
893                        foreach $node (keys %starts) {
894                                if (exists ($ends{$node})) {
895                                        #look for different! ways
896                                        my (@startingWays) = @{$starts{$node}} ;
897                                        my (@endingWays) = @{$ends{$node}} ;
898                                        my $w1 ; my $w2 ;
899                                        loop4:
900                                        foreach $w1 (@startingWays) {
901                                                foreach $w2 (@endingWays) {
902                                                        if ($w1 != $w2) {
903                                                                $wayFound = 1 ;
904                                                                $way1 = $w1 ; 
905                                                                $way2 = $w2 ; # merge w1 and w2
906                                                                #print "start/end: merge ways $way1 and $way2 connected at node $node\n" ;
907                                                                last loop4 ;
908                                                        }
909                                                }
910                                        } # look for ways
911                                        if ($wayFound) {
912                                                #print "way $way1 start $wayStart{$way1} end $wayEnd{$way1}\n" ;
913                                                #print "way $way2 start $wayStart{$way2} end $wayEnd{$way2}\n" ;
914
915                                                # way1 gets new start: start way2
916                                                $wayStart{$way1} = $wayStart{$way2} ;
917                                                my ($way2StartNode) = $wayStart{$way2} ;
918
919                                                # complete
920                                                @{$wayNodes{$way1}} = ( @{$wayNodes{$way2}}[0..$#{$wayNodes{$way2}}-1], @{$wayNodes{$way1}} ) ;
921
922                                                push @{$starts{$way2StartNode}}, $way1 ;
923                                                #print "way $way1 added to starts for node $way2StartNode\n" ;
924
925                                                # remove start way1
926                                                if (scalar (@{$starts{$node}}) == 1) {
927                                                        delete $starts{$node} ;
928                                                        #print "$way1 removed from start hash for node $node\n" ;
929                                                }
930                                                else {
931                                                        @{$starts{$node}} = removeElement ($way1, @{$starts{$node}}) ;
932                                                        #print "$way1 removed from start hash for node $node\n" ;
933                                                }
934
935                                                #remove end way2
936                                                if (scalar (@{$ends{$node}}) == 1) {
937                                                        delete $ends{$node} ;
938                                                        #print "$way2 removed from end hash for node $node\n" ;
939                                                }
940                                                else {
941                                                        @{$ends{$node}} = removeElement ($way2, @{$ends{$node}}) ;
942                                                        #print "$way2 removed from end hash for node $node\n" ;
943                                                }
944                                                #remove start way2
945                                                if (scalar (@{$starts{$way2StartNode}}) == 1) {
946                                                        delete $starts{$way2StartNode} ;
947                                                        #print "$way2 removed from start hash for node $way2StartNode\n" ;
948                                                }
949                                                else {
950                                                        @{$starts{$way2StartNode}} = removeElement ($way2, @{$starts{$way2StartNode}}) ;
951                                                        #print "$way2 removed from start hash for node $way2StartNode\n" ;
952                                                }
953
954                                                # remove way2
955                                                delete $wayEnd{$way2} ;
956                                                delete $wayStart{$way2} ;
957                                                delete $wayNodes{$way2} ;
958                                                #print "way $way2 removed from waystart and wayend hashes\n" ;
959
960                                                #print "\n" ;
961                                                $found = 1 ; 
962                                                last loop3 ;
963                                        }
964                                }
965                        }
966                }
967        }
968
969        # evaluation
970
971
972        #print "\nSUB RESULT\n" ;
973        foreach $way (keys %wayStart) {
974                #print "way $way start $wayStart{$way} end $wayEnd{$way}\n" ;
975                if ($wayStart{$way} != $wayEnd{$way}) {
976                        $openSegments++ ;
977                        #print "   open!\n" ;
978                        push @openEnds, $wayStart{$way}, $wayEnd{$way} ;
979                }
980        }
981        #print "SUB RESULT END\n" ;
982
983        # return complete way
984        if ( (scalar(keys %wayStart) == 1) and ($openSegments == 0) ) {
985                foreach $way1 (keys %wayStart) {
986                        @completeWay = @{$wayNodes{$way1}} ;
987                }
988        }
989        else {
990                @completeWay = () ;
991        }
992
993        return (scalar (keys %wayStart), $openSegments, @completeWay) ;
994}
995
996sub removeElement {
997        # sub removes a single value (once) from an array
998        my ($element, @array) = @_ ;
999        my @arrayNew = () ;
1000        my $pos = -1 ; my $i ;
1001        for ($i=0; $i<=$#array; $i++) { if ($array[$i] == $element) { $pos = $i ; } }
1002        if ($pos != -1) {
1003                if ($pos == 0) {
1004                        @arrayNew = @array[1..$#array] ;
1005                }
1006                if ($pos == $#array) {
1007                        @arrayNew = @array[0..$#array-1] ;
1008                }
1009                if ( ($pos > 0) and ($pos < $#array) ) {
1010                        @arrayNew = @array[0..$pos-1, $pos+1..$#array] ;
1011                }
1012        }
1013        return @arrayNew ;
1014}
1015
1016
1017sub drawPic {
1018        # draws simple picture of relation/boundary. original and possibly simplified/resized boundary.
1019        my ($rel) = shift ;
1020        my $buffer = 0.1 ;
1021        my $lonMin = 999 ;
1022        my $latMin = 999 ;
1023        my $lonMax = -999 ; 
1024        my $latMax = -999 ; 
1025        my $node ;
1026        foreach $node (@{$completeWay{$rel}}) {
1027                if ($lon{$node} > $lonMax) { $lonMax = $lon{$node} ; }
1028                if ($lat{$node} > $latMax) { $latMax = $lat{$node} ; }
1029                if ($lon{$node} < $lonMin) { $lonMin = $lon{$node} ; }
1030                if ($lat{$node} < $latMin) { $latMin = $lat{$node} ; }
1031        }
1032        $lonMin = $lonMin - ($buffer * ($lonMax - $lonMin)) ;
1033        $latMin = $latMin - ($buffer * ($latMax - $latMin)) ;
1034        $lonMax = $lonMax + ($buffer * ($lonMax - $lonMin)) ;
1035        $latMax = $latMax + ($buffer * ($latMax - $latMin)) ;
1036
1037        initGraph ($picSize, $lonMin, $latMin, $lonMax, $latMax) ;
1038       
1039        my @coordinates = () ; my $pt ;
1040        foreach $pt ($relationPolygon{$rel}->points) {
1041                push @coordinates, $pt->[0], $pt->[1] ;
1042        }
1043        drawWay ("green", 3, @coordinates) ;
1044
1045        if ($simplifyOpt eq "1") {     
1046                @coordinates = () ;
1047                foreach $pt ($relationPolygonSimplified{$rel}->points) {
1048                        push @coordinates, $pt->[0], $pt->[1] ;
1049                }
1050                drawWay ("blue", 1, @coordinates) ;
1051        }
1052
1053        if ($resizeOpt eq "1") {       
1054                @coordinates = () ;
1055                foreach $pt ($relationPolygonResized{$rel}->points) {
1056                        push @coordinates, $pt->[0], $pt->[1] ;
1057                }
1058                drawWay ("red", 1, @coordinates) ;
1059        }
1060
1061        drawNodeCircle (center ($relationPolygon{$rel}), "black", 4) ;
1062
1063        drawHead ($program . " ". $version . " by Gary68" . " RelId = " . $rel . " name = " . $relationName{$rel}, "black", 3) ;
1064        drawFoot ("data by openstreetmap.org" . " " . $osmName . " " .ctime(stat($osmName)->mtime), "gray", 3) ;
1065        drawLegend (3, "Center", "black", "Resized", "red", "Simplified", "blue", "Original", "green") ;
1066        drawRuler ("black") ;
1067        writeGraph ($polyBaseName . "." . $rel . ".png") ;
1068}
1069
1070sub getWays {
1071        # sub gets all ways of given relation and all ways of referenced relations, recursive
1072        my ($startingRelation, $level, @relations) = @_ ;
1073        my @result = () ;
1074
1075        if ($verbose) { print "getways called for starting relation $startingRelation with members: @relations\n" ; }
1076        if ($level > $maxNestingLevel) { 
1077                die ("ERROR: relations nested deeper than $maxNestingLevel levels, maybe a loop? starting relation is $startingRelation.\n") ;
1078        }
1079
1080        my $rel ;       
1081        foreach $rel (@relations) {
1082                if (defined ($relationName{$rel})) {
1083                        push @result, @{$relationWays{$rel}} ;
1084                        if (scalar (@{$relationRelations{$rel}}) > 0) {
1085                                my $rel2 ;
1086                                foreach $rel2 (@{$relationRelations{$rel}}) { # could be done without loop, pass whole array...
1087                                        push @result, getWays ($startingRelation, $level+1, $rel2) ;
1088                                }
1089                        }
1090                }
1091                else {
1092                        print "ERROR. Nested relation id=$rel not found or not tagged correctly.\n" ;
1093                }
1094        }
1095        if ($verbose) { print "  getways result: @result\n" ; }
1096        return @result ;
1097}
1098
1099sub isIn {
1100        # check if polygon 1 is in polygon 2 or vice versa
1101        # return 0 = neither
1102        #        1 = p1 is in p2
1103        #        2 = p2 is in p1
1104        my ($p1, $p2) = @_ ;
1105       
1106        if ($debugOpt eq "1") { print "is in called: $p1 $p2\n" ; }
1107
1108        my $p1In2 = 1 ;
1109        first:
1110        foreach my $pt ($p1->points) {
1111                if ($p2->contains ($pt) ) {
1112                        # ok
1113                }
1114                else {
1115                        $p1In2 = 0 ;
1116                        last first ;
1117                }
1118        }
1119
1120        my $p2In1 = 1 ;
1121        second:
1122        foreach my $pt ($p2->points) {
1123                if ($p1->contains ($pt) ) {
1124                        # ok
1125                }
1126                else {
1127                        $p2In1 = 0 ;
1128                        last second ;
1129                }
1130        }
1131
1132        if ($p1In2 == 1) {
1133                return 1 ;
1134        }
1135        elsif ($p2In1 == 1) {
1136                return 2 ;
1137        }
1138        else {
1139                return 0 ;
1140        }
1141
1142}
1143
1144sub center {
1145        my ($polygon) = shift ;
1146        my $lonSum = 0 ;
1147        my $latSum = 0 ;
1148        my $number = 0 ;
1149
1150        foreach my $pt ($polygon->points) {
1151                $lonSum += $pt->[0] ;
1152                $latSum += $pt->[1] ;
1153                $number ++ ;
1154        }
1155        return ($lonSum/$number, $latSum/$number) ;
1156}
Note: See TracBrowser for help on using the repository browser.