source: subversion/applications/utils/gary68/mwMap.pm

Last change on this file was 29704, checked in by lucasvr, 6 years ago

Fixes a rendering issue in which some straight lines would cross the map connecting two points outside the page.
The problem is fixed by invoking coordsOut() to find if certain features need to be rendered.

File size: 30.5 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 mwMap ; 
20
21use strict ;
22use warnings ;
23
24use mwConfig ;
25# use mwMisc ;
26# use mwFile ;
27# use mwLabel ;
28
29use OSM::osm ;
30
31use Geo::Proj4 ;
32
33
34my $areaNum = 0 ;
35my %areaDef = () ;
36
37use vars qw($VERSION @ISA @EXPORT @EXPORT_OK);
38
39require Exporter ;
40
41@ISA = qw ( Exporter AutoLoader ) ;
42
43@EXPORT = qw (  initGraph
44                        coordsOut
45                        drawCircle
46                        drawSquare
47                        drawTriangle
48                        drawDiamond
49                        drawRect
50                        drawText
51                        writeMap
52                        drawWay
53                        drawArea
54                        fitsPaper
55                        getScale
56                        createPath
57                        pathText
58                        placeIcon
59                        convert
60                        gridSquare
61                        getDimensions
62                        initOneways
63                        addOnewayArrows
64                        addAreaIcon
65                        createShield
66                        getMaxShieldSize
67                        getShieldSizes
68                        getShieldId
69                        addToLayer
70                        createLegendFile
71                 ) ;
72
73
74my @belowWays = ("background", "base", "area", "multi") ;
75
76my @aboveWays = ( "arealabels", "wayLabels", "routes", "routeStops", "shields", "nodes", "icons", "text", "additional", "occupied") ;
77
78my @elements = ("scale", "ruler", "legend", "wns", "header", "footer", "rectangles", "title", "gpx") ;
79
80my %svgLayer = () ;
81my %wayLayer = () ;
82
83my $shieldPathId = 0 ;
84my %createdShields = () ;
85my %shieldXSize = () ;
86my %shieldYSize = () ;
87
88my $proj ;
89
90my ($bottom, $left, $right, $top) ;
91my ($sizeX, $sizeY) ;
92my ($projLeft, $projBottom, $projRight, $projTop) ;
93my ($projSizeX, $projSizeY) ;
94
95sub initGraph {
96
97        # function initializes the picture and projection
98
99        my ($x, $l, $b, $r, $t) = @_ ; 
100
101        # my $l0 = int($l) - 1 ;
102        my $l0 = int(($r+$l) / 2 ) ;
103
104        $proj = Geo::Proj4->new(
105                proj => cv('projection'), 
106                ellps => cv('ellipsoid'), 
107                lon_0 => $l0 
108                ) or die "parameter error: ".Geo::Proj4->error. "\n"; 
109
110
111        ($projLeft, $projBottom) = $proj->forward($b, $l) ; # lat/lon!!!
112        ($projRight, $projTop) = $proj->forward($t, $r) ; # lat/lon!!!
113
114        $projSizeX = $projRight - $projLeft ;
115        $projSizeY = $projTop - $projBottom ;
116
117        my $factor = $projSizeY / $projSizeX ;
118
119        $sizeX = int ($x) ;
120        $sizeY = int ($x * $factor) ;
121
122        $top = $t ;
123        $left = $l ;
124        $right = $r ;
125        $bottom = $b ;
126
127        if ( ( cv('bgcolor') ne "none" ) and ( cv('bgcolor') ne "" ) ) {
128                my $col = cv('bgcolor') ;
129                my $svgText = "fill=\"$col\" " ;
130                drawRect (0, 0, $sizeX, $sizeY, 0, $svgText, "background") ;
131        }
132
133        if ( cv('ruler') ne "0" ) {
134                drawRuler() ;
135        }
136
137        if ( cv('scale') ne "0" ) {
138                drawScale() ;
139        }
140
141        if ( cv('grid') != 0) {
142                drawGrid() ;
143        }
144        if ( cv('coords') eq "1") {
145                drawCoords() ;
146        }
147        if ( length cv('foot') > 0 ) {
148                drawFoot() ;
149        }
150        if ( length cv('head') > 0 ) {
151                drawHead() ;
152        }
153
154}
155
156sub addToLayer {
157        my ($layer, $text) = @_ ;
158
159        if ( $layer =~ /^[\d\-\.]+$/) {
160                push @{$wayLayer{$layer}}, $text ;
161                # print "adding NUMERIC: $text\n" ;
162        }
163        else {
164                push @{$svgLayer{$layer}}, $text ;
165                # print "adding TEXTUAL: $text\n" ;
166        }
167}
168
169sub drawWay {
170
171        # accepts list of nodes (plus convert=1)  or list of x,y,x,y (convert=0) and draws way/polygon to layerNr if defined or to layerName
172
173        my ($nodesRef, $convert, $svgString, $layerName, $layerNumber) = @_ ;
174        my @points = () ;
175        my $valid = 0 ;
176
177        # convert? and expand.
178        my ($lonRef, $latRef, $tagRef) = mwFile::getNodePointers() ;
179        if ($convert) {
180                foreach my $node (@$nodesRef) {
181                        my ($x, $y) = convert ( $$lonRef{$node}, $$latRef{$node}) ;
182                        push @points, $x, $y ;
183                        if (! coordsOut ($x, $y)) {
184                                $valid = 1 ;
185                        }
186                }
187        }
188        else {
189                @points = @$nodesRef ;
190                foreach my $node (@$nodesRef) {
191                        my ($x, $y) = ( $$lonRef{$node}, $$latRef{$node}) ;
192                        if (! coordsOut ($x, $y)) {
193                                $valid = 1 ;
194                        }
195                }
196        }
197
198        my $refp = simplifyPoints (\@points) ;
199        @points = @$refp ;
200
201        if ($valid) {
202
203                my $svg = "<polyline points=\"" ;
204                for (my$i=0; $i<scalar(@points)-1; $i+=2) {
205                        $svg = $svg . $points[$i] . "," . $points[$i+1] . " " ;
206                }
207
208                $svg = $svg . "\" $svgString />" ;
209
210                if (defined $layerNumber) {
211                        push @{ $wayLayer{ $layerNumber } }, $svg ;
212                }
213                else {
214                        push @{ $svgLayer { $layerName } }, $svg ;
215                }
216        }
217        else {
218                # if () { print "way not drawn, outside\n" ; }
219        }
220}
221
222
223
224sub drawText {
225
226        my ($x, $y, $convert, $text, $svgString, $layerName) = @_ ;
227
228        if ($convert) {
229                ($x, $y) = convert ($x, $y) ;
230        }
231
232        if ( ! coordsOut ($x, $y) ) {
233
234                my $svg = "<text x=\"$x\" y=\"$y\" $svgString>" . $text . "</text>" ;
235
236                push @{ $svgLayer { $layerName } }, $svg ;
237        }
238}
239
240
241
242
243sub drawCircle {
244
245        # draws circle element to svgLayer given; if convertCoords then lon / lat is converted to x / y
246        # circleradius either in pixel or in meters (convert=1)
247
248        my ($x, $y, $convertCoords, $radius, $convertRadius, $format, $layerName) = @_ ;
249
250        if ($convertCoords) {
251                ($x, $y) = convert ($x, $y) ;
252        }
253        if ($convertRadius) {
254                $radius = $radius / (1000 * distance ($left, $bottom, $right, $bottom) ) * $sizeX ;
255        }
256
257        if ( ! coordsOut ($x, $y) ) {
258
259                my $svg = "<circle cx=\"$x\" cy=\"$y\" r=\"$radius\" " ;
260                $svg .= $format . " />" ;
261
262                push @{ $svgLayer { $layerName } }, $svg ;
263        }
264}
265
266sub drawSquare {
267
268        # draws square element to svgLayer given; if convertCoords then lon / lat is converted to x / y
269        # square size either in pixel or in meters (convert=1)
270
271        my ($x, $y, $convertCoords, $size, $convertSize, $format, $layerName) = @_ ;
272
273        if ($convertCoords) {
274                ($x, $y) = convert ($x, $y) ;
275        }
276        if ($convertSize) {
277                $size = $size / (1000 * distance ($left, $bottom, $right, $bottom) ) * $sizeX ;
278        }
279
280        my $x1 = $x - $size ;
281        my $y1 = $y - $size ;
282        my $dSize = 2 * $size ;
283
284        if ( ! coordsOut ($x, $y) ) {
285
286                my $svg = "<rect x=\"$x1\" y=\"$y1\" width=\"$dSize\" height=\"$dSize\" " ;
287                $svg .= $format . " />" ;
288
289                push @{ $svgLayer { $layerName } }, $svg ;
290        }
291}
292
293sub drawTriangle {
294
295        # draws triangle element to svgLayer given; if convertCoords then lon / lat is converted to x / y
296        # square size either in pixel or in meters (convert=1)
297
298        my ($x, $y, $convertCoords, $size, $convertSize, $format, $layerName) = @_ ;
299
300        if ($convertCoords) {
301                ($x, $y) = convert ($x, $y) ;
302        }
303        if ($convertSize) {
304                $size = $size / (1000 * distance ($left, $bottom, $right, $bottom) ) * $sizeX ;
305        }
306
307        my $h = int ( sqrt ($size * $size / 2)  ) ;
308
309        my $x1 = $x ;
310        # my $y1 = $y - $size ;
311        my $y1 = $y - $h ;
312        my $x2 = $x - $h ;
313        my $y2 = $y + $h ;
314        my $x3 = $x + $h ;
315        my $y3 = $y + $h ;
316
317        if ( ! coordsOut ($x1, $y1, $x2, $y2, $x3, $y3) ) {
318
319                my $svg = "<polyline points=\"$x1,$y1 $x2,$y2 $x3,$y3 $x1,$y1\" " ;
320                $svg .= $format . " />" ;
321
322                push @{ $svgLayer { $layerName } }, $svg ;
323        }
324}
325
326sub drawDiamond {
327
328        # draws diamond element to svgLayer given; if convertCoords then lon / lat is converted to x / y
329        # square size either in pixel or in meters (convert=1)
330
331        my ($x, $y, $convertCoords, $size, $convertSize, $format, $layerName) = @_ ;
332
333        if ($convertCoords) {
334                ($x, $y) = convert ($x, $y) ;
335        }
336        if ($convertSize) {
337                $size = $size / (1000 * distance ($left, $bottom, $right, $bottom) ) * $sizeX ;
338        }
339
340        my $x1 = $x - $size ; # left
341        my $y1 = $y ;
342        my $x2 = $x ; # top
343        my $y2 = $y - $size ;
344        my $x3 = $x + $size ; #right
345        my $y3 = $y ;
346        my $x4 = $x ; # bottom
347        my $y4 = $y + $size ;
348
349        if ( ! coordsOut ($x1, $y1, $x2, $y2, $x3, $y3, $x4, $y4) ) {
350                my $svg = "<polyline points=\"$x1,$y1 $x2,$y2 $x3,$y3 $x4,$y4 $x1,$y1\" " ;
351                $svg .= $format . " />" ;
352
353                push @{ $svgLayer { $layerName } }, $svg ;
354        }
355}
356
357sub drawRect {
358
359        # draws square element to svgLayer given; if convertCoords then lon / lat is converted to x / y
360        # square size either in pixel or in meters (convert=1)
361
362        my ($x1, $y1, $x2, $y2, $convertCoords, $format, $layerName) = @_ ;
363
364        if ($convertCoords) {
365                ($x1, $y1) = convert ($x1, $y1) ;
366                ($x2, $y2) = convert ($x2, $y2) ;
367        }
368
369        my $sizeX = $x2 - $x1 ;
370        my $sizeY = $y2 - $y1 ;
371
372        if ( ! coordsOut ($x1, $y1, $x2, $y2) ) {
373                my $svg = "<rect x=\"$x1\" y=\"$y1\" width=\"$sizeX\" height=\"$sizeY\" " ;
374                $svg .= $format . " />" ;
375
376                push @{ $svgLayer { $layerName } }, $svg ;
377        }
378}
379
380
381sub createPath {
382#
383# creates path element for later use with textPath
384#
385        my ($pathName, $refp, $layer) = @_ ;
386
387        my $refp2 = simplifyPoints ($refp) ;
388        my @points = @$refp2 ;
389
390        my $svg = "<path id=\"" . $pathName . "\" d=\"M " ;
391        my $i ;
392        my $first = 1 ;
393        for ($i=0; $i<scalar(@points); $i+=2) {
394                if ($first) {
395                        $svg = $svg . $points[$i] . "," . $points[$i+1] . " " ;
396                        $first = 0 ;
397                }
398                else {
399                        $svg = $svg . "L " . $points[$i] . "," . $points[$i+1] . " " ;
400                }
401        }
402        $svg = $svg . "\" />\n" ;
403
404        push @{ $svgLayer{ $layer } }, $svg ;
405}
406
407
408sub pathText {
409#
410# draws text to path element; alignment: start, middle, end
411#
412        my ($svgText, $text, $pathName, $tSpan, $alignment, $offset, $layer) = @_ ;
413
414        my $svg = "<text $svgText >\n" ;
415        $svg = $svg . "<textPath xlink:href=\"#" . $pathName . "\" text-anchor=\"" . $alignment . "\" startOffset=\"" . $offset . "%\" >\n" ;
416        $svg = $svg . "<tspan dy=\"" . $tSpan . "\" >" . $text . " </tspan>\n" ;
417        $svg = $svg . "</textPath>\n</text>\n" ;
418
419        push @{ $svgLayer{ $layer } }, $svg ;
420}
421
422
423sub placeIcon {
424#
425# create SVG text for icons
426#
427
428
429        my ($x, $y, $icon, $sizeX, $sizeY, $layer) = @_ ;
430
431        if ( ! coordsOut ($x, $y) ) {
432
433                my ($out) = "<image x=\"" . $x . "\"" ;
434                $out .= " y=\"" . $y . "\"" ;
435                if ($sizeX > 0) { $out .= " width=\"" . $sizeX . "\"" ; }
436                if ($sizeY > 0) { $out .= " height=\"" . $sizeY . "\"" ; }
437                $out .= " xlink:href=\"" . $icon . "\" />" ;
438
439                push @{ $svgLayer{ $layer } }, $out ;
440        }
441}
442
443
444sub drawArea {
445#
446# draws mp in svg ARRAY of ARRAY of nodes/coordinates
447#
448        my ($svgText, $icon, $ref, $convert, $layer) = @_ ;
449        my @ways = @$ref ;
450        my $i ;
451        my $svg = "" ;
452        my $valid = 1 ;
453        my @newArray = () ;
454
455        # TODO loop converts original data !!!
456
457        if ($convert) {
458                my ($lonRef, $latRef, $tagRef) = mwFile::getNodePointers () ;
459                foreach my $aRef (@ways) {
460                        my @way = @$aRef ;
461                        my @newCoords = () ;
462                        foreach my $n (@way) {
463                                my ($x, $y) = convert ($$lonRef{$n}, $$latRef{$n}) ;
464                                push @newCoords, $x, $y ;
465                                if (coordsOut ($x, $y)) {
466                                        $valid = 0 ;
467                                }
468                        }
469                        push @newArray , [@newCoords] ;
470                }
471                @ways = @newArray ;
472        }
473
474        if (defined $areaDef{$icon}) {
475                $svg = "<path $svgText fill-rule=\"evenodd\" style=\"fill:url(" . $areaDef{$icon} . ")\" d=\"" ;
476        }
477        else {
478                $svg = "<path $svgText fill-rule=\"evenodd\" d=\"" ;
479        }
480       
481        foreach my $way (@ways) {
482                my @actual = @$way ;
483                for ($i=0; $i<scalar(@actual); $i+=2) {
484                        if ($i == 0) { $svg .= " M " ; } else { $svg .= " L " ; }
485                        $svg = $svg . $actual[$i] . " " . $actual[$i+1] ;
486                }
487                $svg .= " z" ;
488        }
489
490        $svg = $svg . "\" />" ;
491
492        if ($valid) {
493                push @{ $svgLayer{ $layer } }, $svg ;
494        }
495}
496
497
498
499# ---------------------------------------------------------------------
500
501sub writeMap {
502
503        my $fileName = cv ('out')  ;
504
505        open (my $file, ">", $fileName) || die "can't open svg output file $fileName\n";
506
507        print $file "<?xml version=\"1.0\" encoding=\"utf-8\" standalone=\"no\"?>\n" ;
508        print $file "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\" >\n" ;
509
510        my $w = $sizeX / 300 * 2.54 ; # cm
511        my $h = $sizeY / 300 * 2.54 ;
512
513        my ($svg) = "<svg version=\"1.1\" baseProfile=\"full\" xmlns=\"http://www.w3.org/2000/svg\" " ;
514        $svg .= "xmlns:xlink=\"http://www.w3.org/1999/xlink\" xmlns:ev=\"http://www.w3.org/2001/xml-events\" " ;
515        $svg .= "width=\"$w" . "cm\" height=\"$h" . "cm\" viewBox=\"0 0 $sizeX $sizeY\">\n" ;
516        print $file $svg ;
517
518        # definitions
519        if ( defined @{$svgLayer{'definitions'}} ) {
520                print $file "<defs>\n" ;
521                foreach ( @{$svgLayer{'definitions'}} ) { print $file $_, "\n" ; }
522                print $file "</defs>\n" ;
523        }
524
525        # below ways
526        foreach my $layer (@belowWays) {
527                if ( defined @{$svgLayer{$layer}} ) {
528                        print $file "<g id=\"$layer\">\n" ;
529                        foreach ( @{$svgLayer{$layer}} ) { print $file $_, "\n" ; }
530                        print $file "</g>\n" ;
531                }
532        }
533
534        # ways
535        foreach my $layer (sort {$a <=> $b} keys %wayLayer) {
536                if ( defined @{$wayLayer{$layer}} ) {
537                        print $file "<g id=\"way$layer\">\n" ;
538                        foreach ( @{$wayLayer{$layer}} ) { print $file $_, "\n" ; }
539                        print $file "</g>\n" ;
540                }
541        }
542
543
544        # above of ways
545        foreach my $layer (@aboveWays) {
546                if ( defined @{$svgLayer{$layer}} ) {
547                        print $file "<g id=\"$layer\">\n" ;
548                        foreach ( @{$svgLayer{$layer}} ) { print $file $_, "\n" ; }
549                        print $file "</g>\n" ;
550                }
551        }
552
553
554        foreach my $layer (@elements) {
555                if (defined @{$svgLayer{$layer}}) {
556                        print $file "<g id=\"$layer\">\n" ;
557                        foreach ( @{$svgLayer{$layer}} ) { print $file $_, "\n" ; }
558                        print $file "</g>\n" ;
559                }
560        }
561
562
563        print $file "</svg>\n" ;
564
565        close ($file) ;
566
567        if (cv('pdf') eq "1") {
568                my ($pdfName) = $fileName ;
569                $pdfName =~ s/\.svg/\.pdf/ ;
570                print "creating pdf file $pdfName ...\n" ;
571                `inkscape -A $pdfName $fileName` ;
572        }
573
574        if (cv('png') eq "1") {
575                my ($pngName) = $fileName ;
576                $pngName =~ s/\.svg/\.png/ ;
577                my $dpi = cv('pngdpi') ;
578                print "creating png file $pngName ($dpi dpi)...\n" ;
579                `inkscape --export-dpi=$dpi -e $pngName $fileName` ;
580        }
581
582
583
584}
585
586# -----------------------------------------------------------------------------------
587
588sub drawGrid {
589#
590# draw grid on top of map. receives number of parts in x/lon direction
591#
592
593        my $number = cv ('grid') ;
594        my $color = cv ('gridcolor') ;
595
596        my $part = $sizeX / $number ;
597        my $numY = $sizeY / $part ;
598
599        my $svgStringLine="stroke=\"$color\" stroke-width=\"5\" stroke-dasharray=\"30,30\"" ;
600
601        my $svgStringText = mwMisc::createTextSVG ( cv('elementFontFamily'), cv('elementFont'), undef, undef, 60, $color, undef, undef) ;
602
603        # vertical lines
604        for (my $i = 1; $i <= $number; $i++) {
605                my @coords = ($i*$part, 0, $i*$part, $sizeY) ;
606                drawWay (\@coords, 0, $svgStringLine, "additional", undef) ;
607                drawText ( ($i-1)*$part+$part/2, 160, 0, chr($i+64), $svgStringText, "additional") ;
608
609        }
610
611        # hor. lines
612        for (my $i = 1; $i <= $numY; $i++) {
613                my @coords = (0, $i*$part, $sizeX, $i*$part) ;
614                drawWay (\@coords, 0, $svgStringLine, "additional", undef) ;
615                drawText ( 20, ($i-1)*$part+$part/2, 0, $i, $svgStringText, "additional") ;
616
617        }
618}
619
620sub drawCoords {
621#
622# draws coordinates grid on map
623#
624        my $exp = cv('coordsexp') ; 
625        my $color = cv ('coordscolor');
626        my $step = 10 ** $exp ;
627
628        # vert. lines
629        my $start = int ($left / $step) + 1 ;
630        my $actual = $start * $step ;
631
632        my $svgStringLine="stroke=\"$color\" stroke-width=\"3\"" ;
633        my $svgStringText = mwMisc::createTextSVG ( cv('elementFontFamily'), cv('elementFont'), undef, undef, 30, $color, undef, undef) ;
634
635        while ($actual < $right) {
636                my ($x1, $y1) = convert ($actual, 0) ;
637
638                drawText ( $x1+10, $sizeY-50, 0, $actual, $svgStringText, "additional") ;
639
640                my @coords = ($x1, 0, $x1, $sizeY) ;
641                drawWay (\@coords, 0, $svgStringLine, "additional", undef) ;
642
643                $actual += $step ;
644        }
645
646        # hor lines
647        $start = int ($bottom / $step) + 1 ;
648        $actual = $start * $step ;
649        while ($actual < $top) {
650                # print "actualY: $actual\n" ;
651                my ($x1, $y1) = convert (0, $actual) ;
652
653                drawText ( $sizeX-180, $y1+30, 0, $actual, $svgStringText, "additional") ;
654
655                my @coords = (0, $y1, $sizeX, $y1) ;
656                drawWay (\@coords, 0, $svgStringLine, "additional", undef) ;
657
658                $actual += $step ;
659        }
660}
661
662
663
664
665# -----------------------------------------------------------------------------------
666
667sub convert {
668
669        # converts real world coordinates to system graph pixel coordinates
670
671        my ($x, $y) = @_ ;
672
673        my ($x1, $y1) = $proj->forward($y, $x) ; # lat/lon!!!
674
675        my $x2 = int ( ($x1 - $projLeft) / ($projRight - $projLeft) * $sizeX ) ;
676        my $y2 = $sizeY - int ( ($y1 - $projBottom) / ($projTop - $projBottom) * $sizeY ) ;
677
678        return ($x2, $y2) ;
679}
680
681sub simplifyPoints {
682        my $ref = shift ;
683        my @points = @$ref ;
684        my @newPoints ;
685        my $maxIndex = $#points ;
686
687        if (scalar @points > 4) {
688                # push first
689                push @newPoints, $points[0], $points[1] ;
690
691                # push other
692                for (my $i=2; $i <= $maxIndex; $i+=2) {
693                        # $simplifyTotal++ ;
694                        if ( ($points[$i]==$points[$i-2]) and ($points[$i+1]==$points[$i-1]) ) {
695                                # same
696                                # $simplified++ ;
697                        }
698                        else {
699                                push @newPoints, $points[$i], $points[$i+1] ;
700                        }
701                }
702                return (\@newPoints) ;
703        }
704        else {
705                return ($ref) ;
706        }
707
708}
709
710sub drawRuler {
711#
712# draws ruler
713#
714        my $col = cv('rulercolor') ;   
715
716        my $B ; my $B2 ;
717        my $L ; my $Lpix ;
718        my $x ;
719        my $text ;
720
721        my $lineThickness = 8 ; # at 300dpi
722        my $textSize = 40 ; # at 300 dpi
723        my $textDist = 60 ; # at 300 dpi
724        my $lineLen = 40 ; # at 300 dpi
725
726        my $xOffset = 2 * $lineThickness ;
727        my $yOffset = 2 * $lineThickness ;
728               
729        $B = $right - $left ;                           # in degrees
730        $B2 = $B * cos ($top/360*3.14*2) * 111.1 ;      # in km
731        $text = "50m" ; $x = 0.05 ;                     # default length ruler
732
733        if ($B2 > 0.5) {$text = "100 m" ; $x = 0.1 ; }  # enlarge ruler
734        if ($B2 > 1) {$text = "500 m" ; $x = 0.5 ; }    # enlarge ruler
735        if ($B2 > 5) {$text = "1 km" ; $x = 1 ; }
736        if ($B2 > 10) {$text = "5 km" ; $x = 5 ; }
737        if ($B2 > 50) {$text = "10 km" ; $x = 10 ; }
738        $L = $x / (cos ($top/360*3.14*2) * 111.1 ) ;    # length ruler in km
739        $Lpix = $L / $B * $sizeX ;                      # length ruler in pixels
740
741        my $rSizeX = int ($Lpix + 2 * $xOffset) ;
742        my $rSizeY = int ($lineLen + $textSize + 3 * $yOffset) ;
743        addToLayer ("definitions", "<g id=\"rulerdef\" width=\"$rSizeX\" height=\"$rSizeY\" >") ;
744
745        if ( cv('rulerbackground') ne "none" ) {
746                my $color = cv ('rulerbackground') ;
747                my $svgString = "fill=\"$color\"" ;
748                drawRect (0, 0, $rSizeX, $rSizeY, 0, $svgString, "definitions") ;
749        }
750
751        my $svgString = "stroke=\"$col\" stroke-width=\"$lineThickness\" stroke-linecap=\"round\" " ;
752
753        my @coords = ($xOffset, $yOffset, $xOffset+$Lpix, $yOffset) ;
754        drawWay (\@coords, 0, $svgString, "definitions", undef) ;
755
756        @coords = ($xOffset, $yOffset, $xOffset, $yOffset+$lineLen) ;
757        drawWay (\@coords, 0, $svgString, "definitions", undef) ;
758
759        @coords = ($xOffset+$Lpix, $yOffset, $xOffset+$Lpix, $yOffset+$lineLen) ;
760        drawWay (\@coords, 0, $svgString, "definitions", undef) ;
761
762        @coords = ($xOffset+$Lpix/2, $yOffset, $xOffset+$Lpix/2, $yOffset+$lineLen/2) ;
763        drawWay (\@coords, 0, $svgString, "definitions", undef) ;
764
765
766        my $scale= getScale() ;
767        $text .= "(1:$scale)" ;
768
769
770        $svgString = mwMisc::createTextSVG ( cv('elementFontFamily'), cv('elementFont'), undef, undef, 35, $col, undef, undef) ;
771        drawText ($xOffset, $yOffset+$textDist+30, 0, $text, $svgString, "definitions") ;
772
773        addToLayer ("definitions", "</g>") ;
774
775        my $posX = 40 ; my $posY = 40 ;
776
777        if ( cv('ruler') eq "2") {
778                $posX = $sizeX - 40 - $rSizeX ;
779                $posY = 40 ;
780        }
781
782        if ( cv('ruler') eq "3") {
783                $posX = 40 ;
784                $posY = $sizeY - 40 - $rSizeY ;
785        }
786
787        if ( cv('ruler') eq "4") {
788                $posX = $sizeX - 40 - $rSizeX ;
789                $posY = $sizeY - 40 - $rSizeY ;
790        }
791
792        addToLayer ("ruler", "<use x=\"$posX\" y=\"$posY\" xlink:href=\"#rulerdef\" />") ;
793}
794
795sub drawScale {
796#
797# draws scale value
798#
799        my $col = cv('scalecolor') ;   
800
801        my $xOffset = 20 ;
802        my $yOffset = 20 ;
803        my $fontSize = 70 ;             
804        my $borderDist = 60 ;
805
806        my $rSizeX = int (350 + 2 * $xOffset) ;
807        my $rSizeY = int ($fontSize + 2 * $yOffset) ;
808        addToLayer ("definitions", "<g id=\"scaledef\" width=\"$rSizeX\" height=\"$rSizeY\" >") ;
809
810        if ( cv('scalebackground') ne "none" ) {
811                my $color = cv ('scalebackground') ;
812                my $svgString = "fill=\"$color\"" ;
813                drawRect (0, 0, $rSizeX, $rSizeY, 0, $svgString, "definitions") ;
814        }
815
816        my $scale= getScale() ;
817
818        my $svgString = mwMisc::createTextSVG ( cv('elementFontFamily'), cv('elementFont'), undef, undef, $fontSize, $col, undef, undef) ;
819        drawText ($xOffset, $fontSize + $yOffset, 0, "1:$scale", $svgString, "definitions") ;
820
821        addToLayer ("definitions", "</g>") ;
822
823        my $posX = $borderDist ; my $posY = $borderDist ;
824
825        if ( cv('scale') eq "2") {
826                $posX = $sizeX - $borderDist - $rSizeX ;
827                $posY = $borderDist ;
828        }
829
830        if ( cv('scale') eq "3") {
831                $posX = $borderDist ;
832                $posY = $sizeY - $borderDist - $rSizeY ;
833        }
834
835        if ( cv('scale') eq "4") {
836                $posX = $sizeX - $borderDist - $rSizeX ;
837                $posY = $sizeY - $borderDist - $rSizeY ;
838        }
839
840        addToLayer ("scale", "<use x=\"$posX\" y=\"$posY\" xlink:href=\"#scaledef\" />") ;
841}
842
843sub drawFoot {
844#
845# draws footer
846#
847        my $col = cv('footcolor') ;     
848        my $text = cv('foot') ; 
849        my $len = length $text ;
850
851        my $xOffset = 20 ;
852        my $yOffset = 20 ;
853        my $fontSize = cv('footsize') ;         
854        my $borderDistX = 60 ;
855        my $borderDistY = $fontSize + 50 ;
856
857        my $rSizeX = int ($len*cv('ppc')/10*$fontSize + 2 * $xOffset) ;
858        my $rSizeY = int ($fontSize + 2 * $yOffset) ;
859        addToLayer ("definitions", "<g id=\"footdef\" width=\"$rSizeX\" height=\"$rSizeY\" >") ;
860
861        if ( cv('footbackground') ne "none" ) {
862                my $color = cv ('footbackground') ;
863                my $svgString = "fill=\"$color\"" ;
864                drawRect (0, 0, $rSizeX, $rSizeY, 0, $svgString, "definitions") ;
865        }
866
867        my $svgString = mwMisc::createTextSVG ( cv('elementFontFamily'), cv('elementFont'), undef, undef, $fontSize, $col, undef, undef) ;
868        drawText ($xOffset, $fontSize + $yOffset, 0, $text, $svgString, "definitions") ;
869
870        addToLayer ("definitions", "</g>") ;
871
872        my $posX = $borderDistX ; my $posY = $sizeY - $borderDistY ;
873
874        addToLayer ("footer", "<use x=\"$posX\" y=\"$posY\" xlink:href=\"#footdef\" />") ;
875}
876
877sub drawHead {
878#
879# draws header
880#
881        my $col = cv('headcolor') ;     
882        my $text = cv('head') ; 
883        my $len = length $text ;
884
885        my $xOffset = 20 ;
886        my $yOffset = 20 ;
887        my $fontSize = cv('headsize') ;         
888        my $borderDistX = 60 ;
889        my $borderDistY = 60 ;
890
891        my $rSizeX = int ($len*cv('ppc')/10*$fontSize + 2 * $xOffset) ;
892        my $rSizeY = int ($fontSize + 2 * $yOffset) ;
893        addToLayer ("definitions", "<g id=\"headdef\" width=\"$rSizeX\" height=\"$rSizeY\" >") ;
894
895        if ( cv('headbackground') ne "none" ) {
896                my $color = cv ('headbackground') ;
897                my $svgString = "fill=\"$color\"" ;
898                drawRect (0, 0, $rSizeX, $rSizeY, 0, $svgString, "definitions") ;
899        }
900
901        my $svgString = mwMisc::createTextSVG ( cv('elementFontFamily'), cv('elementFont'), undef, undef, $fontSize, $col, undef, undef) ;
902        drawText ($xOffset, $fontSize + $yOffset, 0, $text, $svgString, "definitions") ;
903
904        addToLayer ("definitions", "</g>") ;
905
906        my $posX = $borderDistX ; my $posY = $borderDistY ;
907
908        addToLayer ("header", "<use x=\"$posX\" y=\"$posY\" xlink:href=\"#headdef\" />") ;
909}
910
911
912sub fitsPaper {
913#
914# calculates on what paper size the map will fit. sizes are taken from global variables
915#
916
917        my $width = $sizeX / 300 * 2.54 ;
918        my $height = $sizeY / 300 * 2.54 ;
919        my $paper = "" ;
920
921        my @sizes = () ;
922        push @sizes, ["4A0", 168.2, 237.8] ;
923        push @sizes, ["2A0", 118.9, 168.2] ;
924        push @sizes, ["A0", 84.1, 118.9] ;
925        push @sizes, ["A1", 59.4, 84.1] ;
926        push @sizes, ["A2", 42, 59.4] ;
927        push @sizes, ["A3", 29.7, 42] ;
928        push @sizes, ["A4", 21, 29.7] ;
929        push @sizes, ["A5", 14.8, 21] ;
930        push @sizes, ["A6", 10.5, 14.8] ;
931        push @sizes, ["A7", 7.4, 10.5] ;
932        push @sizes, ["none", 0, 0] ;
933
934        foreach my $size (@sizes) {
935                if ( ( ($width<=$size->[1]) and ($height<=$size->[2]) ) or ( ($width<=$size->[2]) and ($height<=$size->[1]) ) ) {
936                        $paper = $size->[0] ;
937                }
938        }
939
940        return ($paper, $width, $height) ;
941}
942
943sub getScale {
944#
945# calcs scale of map
946#
947        my ($dpi) = 300 ;
948
949        my $dist = distance ($left, $bottom, $right, $bottom) ;
950        my $inches = $sizeX / $dpi ;
951        my $cm = $inches * 2.54 ;
952        my $scale = int ( $dist / ($cm/100/1000)  ) ;
953        $scale = int ($scale / 100) * 100 ;
954
955        return ($scale) ;
956}
957
958sub gridSquare {
959#
960# returns grid square of given coordinates for directories
961#
962        my ($lon, $lat) = @_ ;
963
964        my $parts = cv('grid') ;
965
966        my ($x, $y) = convert ($lon, $lat) ;
967        my $xi = int ($x / ($sizeX / $parts)) + 1 ;
968        my $yi = int ($y / ($sizeX / $parts)) + 1 ;
969        if ( ($x >= 0) and ($x <= $sizeX) and ($y >= 0) and ($y <= $sizeY) ) {
970                return (chr($xi+64) . $yi) ;
971        }
972        else {
973                return undef ;
974        }
975}
976
977
978sub getDimensions {
979        return ($sizeX, $sizeY) ;
980}
981
982# ----------------------------------------------------------------------
983
984sub initOneways {
985#
986# write marker defs to svg
987#
988        my $color = cv('onewaycolor') ;
989
990        my @svgOutputDef = () ;
991        for (my $markerSize = 5; $markerSize <= 40; $markerSize++) {
992                push @svgOutputDef, "<marker id=\"Arrow$markerSize\"" ;
993                push @svgOutputDef, "viewBox=\"0 0 10 10\" refX=\"5\" refY=\"5\"" ;
994                push @svgOutputDef, "markerUnits=\"strokeWidth\"" ;
995                push @svgOutputDef, "markerWidth=\"" . $markerSize . "\" markerHeight=\"" . $markerSize . "\"" ;
996                push @svgOutputDef, "orient=\"auto\">" ;
997                push @svgOutputDef, "<path d=\"M 0 4 L 6 4 L 6 2 L 10 5 L 6 8 L 6 6 L 0 6 Z\" fill=\"" . $color .  "\" />" ;
998                push @svgOutputDef, "</marker>" ;
999        }
1000
1001
1002        foreach my $line (@svgOutputDef) {
1003                addToLayer ("definitions", $line) ;
1004        }
1005}
1006
1007sub addOnewayArrows {
1008#
1009# adds oneway arrows to new pathes
1010#
1011        my ($wayNodesRef, $direction, $thickness, $layer) = @_ ;
1012        my @wayNodes = @$wayNodesRef ;
1013        my ($lonRef, $latRef) = mwFile::getNodePointers() ;
1014
1015        if ($direction == -1) { @wayNodes = reverse @wayNodes ; }
1016
1017        my $minDist = cv('onewaysize') * 1.5 ;
1018        my $markerSize = cv('onewaySize') ;
1019
1020        if ( cv('onewayAutoSize') != 0 ) {
1021                $markerSize = int ( $thickness / 100 * cv('onewayAutoSize') ) ;
1022                if ( $markerSize < 5 ) { $markerSize = 5 ; }
1023                if ( $markerSize > 40 ) { $markerSize = 40 ; }
1024                $minDist = $markerSize * 1.5 ;
1025        }
1026
1027        # create new pathes with new nodes
1028        for (my $i = 0; $i < scalar( @wayNodes ) - 1; $i++ ) {
1029                my ($x1, $y1) = convert ($$lonRef{$wayNodes[$i]}, $$latRef{$wayNodes[$i]}) ;
1030                my ($x2, $y2) = convert ($$lonRef{$wayNodes[$i+1]}, $$latRef{$wayNodes[$i+1]}) ;
1031                my $xn = ($x2+$x1) / 2 ;
1032                my $yn = ($y2+$y1) / 2 ;
1033                if (sqrt (($x2-$x1)**2+($y2-$y1)**2) > $minDist) {
1034                        # create path
1035                        # use path
1036                        my $svg = "<path d=\"M $x1 $y1 L $xn $yn L $x2 $y2\" fill=\"none\" marker-mid=\"url(#Arrow$markerSize)\" />" ;
1037                       
1038                        addToLayer ($layer+$thickness/100, $svg) ;
1039                }
1040        }
1041}
1042
1043# ----------------------------------------------------------------------------
1044
1045sub addAreaIcon {
1046#
1047# initial collection of area icons
1048#
1049        my $fileNameOriginal = shift ;
1050        # print "AREA: $fileNameOriginal\n" ;
1051        my $result = open (my $file, "<", $fileNameOriginal) ;
1052        close ($file) ;
1053        if ($result) {
1054                my ($x, $y) ;
1055                if (grep /.svg/, $fileNameOriginal) {
1056                        ($x, $y) = mwMisc::sizeSVG ($fileNameOriginal) ;
1057                        if ( ($x == 0) or ($y == 0) ) { 
1058                                $x = 32 ; $y = 32 ; 
1059                                print "WARNING: size of file $fileNameOriginal could not be determined. Set to 32px x 32px\n" ;
1060                        } 
1061                }
1062
1063                if (grep /.png/, $fileNameOriginal) {
1064                        ($x, $y) = mwMisc::sizePNG ($fileNameOriginal) ;
1065                }
1066
1067                if (!defined $areaDef{$fileNameOriginal}) {
1068
1069                        my $x1 = $x ; # scale area icons
1070                        my $y1 = $y ;
1071                        my $fx = $x1 / $x ;
1072                        my $fy = $y1 / $y ;
1073                       
1074                        # add defs to svg output
1075                        my $defName = "A" . $areaNum ;
1076                        # print "INFO area icon $fileNameOriginal, $defName, $x, $y --- $x1, $y1 --- $fx, $fy --- processed.\n" ;
1077                        $areaNum++ ;
1078
1079                        my $svgElement = "<pattern id=\"" . $defName . "\" width=\"" . $x . "\" height=\"" . $y . "\" " ;
1080                        $svgElement .= "patternTransform=\"translate(0,0) scale(" . $fx . "," . $fy . ")\" \n" ;
1081                        $svgElement .= "patternUnits=\"userSpaceOnUse\">\n" ;
1082                        $svgElement .= "  <image xlink:href=\"" . $fileNameOriginal . "\"/>\n" ;
1083                        $svgElement .= "</pattern>\n" ;
1084
1085                        addToLayer ("definitions", $svgElement) ;
1086
1087                        $defName = "#" . $defName ;
1088                        $areaDef{$fileNameOriginal} = $defName ;
1089                }
1090        }
1091        else {
1092                print "WARNING: area icon $fileNameOriginal not found!\n" ;
1093        }
1094}
1095
1096# ----------------------------------------------------------------------------
1097
1098sub createShield {
1099        my ($name, $targetSize) = @_ ;
1100        my @a = split /:/, $name ;
1101        my $shieldFileName = $a[1] ;
1102        my $shieldText = $a[2] ;
1103
1104        if (! defined $createdShields{$name}) {
1105                open (my $file, "<", $shieldFileName) or die ("ERROR: shield definition $shieldFileName not found.\n") ;
1106                my @defText = <$file> ;
1107                close ($file) ;
1108
1109                # get size
1110                # calc scaling
1111                my $sizeX = 0 ;
1112                my $sizeY = 0 ;
1113                foreach my $line (@defText) {
1114                        if (grep /<svg/, $line) {
1115                                ($sizeY) = ( $line =~ /height=\"(\d+)px\"/ ) ;
1116                                ($sizeX) = ( $line =~ /width=\"(\d+)px\"/ ) ;
1117                                if ( (!defined $sizeX) or (!defined $sizeY) ) {
1118                                        die "ERROR: size of shield in $shieldFileName could not be determined.\n" ;
1119                                }
1120                        }
1121                }
1122                if ( ($sizeX == 0) or ($sizeY == 0) ) {
1123                        die "ERROR: initial size of shield $shieldFileName could not be determined.\n" ;
1124                }
1125
1126                my $scaleFactor = $targetSize / $sizeY ;
1127
1128                $shieldXSize{ $name } = int ($sizeX * $scaleFactor) ;
1129                $shieldYSize{ $name } = int ($sizeY * $scaleFactor) ;
1130
1131                $shieldPathId++ ;
1132                my $shieldPathName = "ShieldPath" . $shieldPathId ;
1133                my $shieldGroupName = "ShieldGroup" . $shieldPathId ;
1134
1135                foreach my $line (@defText) {
1136                        $line =~ s/REPLACEID/$shieldGroupName/ ;
1137                        $line =~ s/REPLACESCALE/$scaleFactor/g ;
1138                        $line =~ s/REPLACEPATH/$shieldPathName/ ;
1139                        $line =~ s/REPLACELABEL/$shieldText/ ;
1140                }
1141
1142                foreach my $line (@defText) {
1143                        addToLayer ("definitions", $line) ;
1144                }
1145
1146                $createdShields{$name} = $shieldGroupName ;
1147        }
1148}
1149
1150sub getMaxShieldSize {
1151        my $name = shift ;
1152        my $max = $shieldXSize{$name} ;
1153        if ( $shieldYSize{$name} > $max) { $max = $shieldYSize{$name} ; }
1154        return $max ;
1155}
1156
1157sub getShieldSizes {
1158        my $name = shift ;
1159        my $x = $shieldXSize{$name} ;
1160        my $y = $shieldYSize{$name} ;
1161        return $x, $y ;
1162}
1163
1164sub getShieldId {
1165        my $name = shift ;
1166        return $createdShields{$name} ;
1167}
1168
1169# --------------------------------------------------------------------
1170
1171sub createLegendFile {
1172        my ($x, $y, $extension, $group) = @_ ;
1173
1174        my $svgName = cv('out') ;
1175        $svgName =~ s/\.svg/$extension\.svg/i ; 
1176        my $pngName = $svgName ;
1177        $pngName =~ s/\.svg/\.png/i ;
1178        my $pdfName = $svgName ;
1179        $pdfName =~ s/\.svg/\.pdf/i ;
1180
1181
1182        open (my $file, ">", $svgName) || die "can't open legend svg output file $svgName\n";
1183
1184        print $file "<?xml version=\"1.0\" encoding=\"utf-8\" standalone=\"no\"?>\n" ;
1185        print $file "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\" >\n" ;
1186
1187        my $w = $x / 300 * 2.54 ; # cm
1188        my $h = $y / 300 * 2.54 ;
1189
1190        my ($svg) = "<svg version=\"1.1\" baseProfile=\"full\" xmlns=\"http://www.w3.org/2000/svg\" " ;
1191        $svg .= "xmlns:xlink=\"http://www.w3.org/1999/xlink\" xmlns:ev=\"http://www.w3.org/2001/xml-events\" " ;
1192        $svg .= "width=\"$w" . "cm\" height=\"$h" . "cm\" viewBox=\"0 0 $x $y\">\n" ;
1193        print $file $svg ;
1194
1195        print $file "<defs>\n" ;
1196        foreach ( @{$svgLayer{'definitions'}} ) { print $file $_, "\n" ; }
1197        print $file "</defs>\n" ;
1198
1199        print $file "<use x=\"0\" y=\"0\" xlink:href=\"$group\" />\n" ;
1200        print $file "</svg>\n" ;
1201        close $file ;
1202
1203        if (cv('pdf') eq "1") {
1204                print "creating pdf file $pdfName ...\n" ;
1205                `inkscape -A $pdfName $svgName` ;
1206        }
1207
1208        if (cv('png') eq "1") {
1209                my $dpi = cv('pngdpi') ;
1210                print "creating png file $pngName ($dpi dpi)...\n" ;
1211                `inkscape --export-dpi=$dpi -e $pngName $svgName` ;
1212        }
1213}
1214
1215sub coordsOut {
1216        my @points = @_ ;
1217
1218        my $allOut = 0 ;
1219
1220        my $outLeft = 1 ;
1221        my $outRight = 1 ;
1222        my $outTop = 1 ;
1223        my $outBottom = 1 ;
1224
1225        for (my $i=0; $i < scalar(@points) - 1; $i += 2) {
1226                my $x = $points[$i] ;
1227                my $y = $points[$i+1] ;
1228                if ($x >= 0) { $outLeft = 0 ; }
1229                if ($x <= $sizeX) { $outRight = 0 ; }
1230                if ($y >= 0) { $outBottom = 0 ; }
1231                if ($y <= $sizeY) { $outTop = 0 ; }
1232        }
1233
1234        if ( ($outLeft) or ($outRight) or ($outTop) or ($outBottom) ) { $allOut = 1 ; }
1235
1236        return $allOut ;
1237}
1238
12391 ;
1240
1241
Note: See TracBrowser for help on using the repository browser.