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

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

mapweaver: small svg bux fixed

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