source: subversion/applications/utils/gary68/mwLabel.pm @ 26271

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

new mapweaver version; dir.pl updated

File size: 13.2 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 mwLabel ; 
20
21use strict ;
22use warnings ;
23
24use mwConfig ;
25use mwMap ;
26use mwMisc ;
27
28use OSM::QuadTree ;
29
30my $labelPathId = 0 ;
31
32my $qtWayLabels ;
33my $qtPoiLabels ;
34my @occupiedAreas = () ;
35my @lines = () ;
36
37my $numIconsMoved = 0 ;
38my $numLabels = 0 ;
39my $numIcons = 0 ;
40my $numLabelsOmitted = 0 ;
41my $numLabelsMoved = 0 ;
42my $numIconsOmitted = 0 ;
43
44my %poiHash = () ;
45
46use vars qw($VERSION @ISA @EXPORT @EXPORT_OK);
47
48require Exporter ;
49
50@ISA = qw ( Exporter AutoLoader ) ;
51
52@EXPORT = qw (
53                        placeLabelAndIcon
54                        initQuadTrees
55                        occupyLines
56                        lineCrossings
57                        addToPoiHash
58                        getPoiHash
59                 ) ;
60
61
62sub initQuadTrees {
63        my ( $sizeX, $sizeY ) = @_ ;
64        $qtWayLabels = OSM::QuadTree->new(  -xmin  => 0,
65                                      -xmax  => $sizeX+100,
66                                      -ymin  => 0,
67                                      -ymax  => $sizeY+40,
68                                      -depth => 5);
69        $qtPoiLabels = OSM::QuadTree->new(  -xmin  => 0,
70                                      -xmax  => $sizeX+100,
71                                      -ymin  => 0,
72                                      -ymax  => $sizeY+40,
73                                      -depth => 5);
74}
75
76
77sub placeLabelAndIcon {
78#
79# intelligent icon and label placement alg.
80#
81        my ($lon, $lat, $offset, $thickness, $text, $svgText, $icon, $iconSizeX, $iconSizeY, $layer) = @_ ;
82
83        if (cv('debug') eq "1") { print "PLAI: $lon, $lat, $offset, $thickness, $text, $svgText, $icon, $iconSizeX, $iconSizeY, $layer\n" ; }
84
85        my ($x, $y) = mwMap::convert ($lon, $lat) ; # center !
86        $y = $y + $offset ;
87
88        my ($ref) = splitLabel ($text) ;
89        my (@lines) = @$ref ;
90        my $numLines = scalar @lines ;
91        my $maxTextLenPix = 0 ;
92        my $orientation = "" ;
93        my $lineDist = cv ('linedist') ; ;
94        my $tries = 0 ;
95        my $allowIconMove = cv ('allowiconmove') ;
96
97        my ($textSize) = ( $svgText =~ /font-size=\"(\d+)\"/ ) ;
98        if ( ! defined $textSize ) { die ("ERROR: font size could not be determined from svg format string \"$svgText\"\n") ; }
99
100        foreach my $line (@lines) {
101                my $len = length ($line) * cv('ppc') / 10 * $textSize ; # in pixels
102                if ($len > $maxTextLenPix) { $maxTextLenPix = $len ; }
103        }
104        my $spaceTextX = $maxTextLenPix ;
105        my $spaceTextY = $numLines * ($lineDist+$textSize) ;
106
107
108        if ($icon ne "none") {
109                $numIcons++ ;
110                # space for icon?
111                        my $sizeX1 = $iconSizeX ; if ($sizeX1 == 0) { $sizeX1 = 20 ; }
112                        my $sizeY1 = $iconSizeY ; if ($sizeY1 == 0) { $sizeY1 = 20 ; }
113                        my $iconX = $x - $sizeX1/2 ; # top left corner
114                        my $iconY = $y - $sizeY1/2 ; 
115
116                        my @shifts = (0) ;
117                        if ($allowIconMove eq "1") {
118                                @shifts = ( 0, -15, 15 ) ;
119                        }
120                        my $posFound = 0 ; my $posCount = 0 ;
121                        LABAB: foreach my $xShift (@shifts) {
122                                foreach my $yShift (@shifts) {
123                                        $posCount++ ;
124                                        if ( ! areaOccupied ($iconX+$xShift, $iconX+$sizeX1+$xShift, $iconY+$sizeY1+$yShift, $iconY+$yShift) ) {
125                                                placeIcon ($iconX+$xShift, $iconY+$yShift, $icon, $sizeX1, $sizeY1, "nodes") ;
126                                                occupyArea ($iconX+$xShift, $iconX+$sizeX1+$xShift, $iconY+$sizeY1+$yShift, $iconY+$yShift) ;
127                                                $posFound = 1 ;
128                                                if ($posCount > 1) { $numIconsMoved++ ; }
129                                                $iconX = $iconX + $xShift ; # for later use with label
130                                                $iconY = $iconY + $yShift ;
131                                                last LABAB ;
132                                        }
133                                }
134                        }
135                        if ($posFound == 1) {
136
137                                # label text?
138                                if ($text ne "") {
139                                        $numLabels++ ;
140
141
142                                        $sizeX1 += 1 ; $sizeY1 += 1 ;
143
144                                        my ($x1, $x2, $y1, $y2) ;
145                                        # $x, $y centered
146                                        # yes, check if space for label, choose position, draw
147                                        # no, count omitted text
148
149                                        my @positions = () ; my $positionFound = 0 ;
150                                        # pos 1 centered below
151                                        $x1 = $x - $spaceTextX/2 ; $x2 = $x + $spaceTextX/2 ; $y1 = $y + $sizeY1/2 + $spaceTextY ; $y2 = $y + $sizeY1/2 ; $orientation = "centered" ; 
152                                        push @positions, [$x1, $x2, $y1, $y2, $orientation] ;
153
154                                        # pos 2/3 to the right, bottom, top
155                                        $x1 = $x + $sizeX1/2 ; $x2 = $x + $sizeX1/2 + $spaceTextX ; $y1 = $y + $sizeY1/2 ; $y2 = $y1 - $spaceTextY ; $orientation = "left" ; 
156                                        push @positions, [$x1, $x2, $y1, $y2, $orientation] ;
157                                        $x1 = $x + $sizeX1/2 ; $x2 = $x + $sizeX1/2 + $spaceTextX ; $y2 = $y - $sizeY1/2 ; $y1 = $y2 + $spaceTextY ; $orientation = "left" ; 
158                                        push @positions, [$x1, $x2, $y1, $y2, $orientation] ;
159
160                                        # pos 4 centered upon
161                                        $x1 = $x - $spaceTextX/2 ; $x2 = $x + $spaceTextX/2 ; $y1 = $y - $sizeY1/2 ; $y2 = $y - $sizeY1/2 - $spaceTextY ; $orientation = "centered" ; 
162                                        push @positions, [$x1, $x2, $y1, $y2, $orientation] ;
163
164                                        # pos 5/6 to the right, below and upon
165                                        $x1 = $x + $sizeX1/2 ; $x2 = $x + $sizeX1/2 + $spaceTextX ; $y2 = $y + $sizeY1/2 ; $y1 = $y2 + $spaceTextY ; $orientation = "left" ; 
166                                        push @positions, [$x1, $x2, $y1, $y2, $orientation] ;
167                                        $x1 = $x + $sizeX1/2 ; $x2 = $x + $sizeX1/2 + $spaceTextX ; $y1 = $y - $sizeY1/2 ; $y2 = $y1 - $spaceTextY ; $orientation = "left" ; 
168                                        push @positions, [$x1, $x2, $y1, $y2, $orientation] ;
169
170                                        # left normal, bottom, top
171                                        $x1 = $x - $sizeX1/2 - $spaceTextX ; $x2 = $x - $sizeX1/2 ; $y1 = $y + $sizeY1/2 ; $y2 = $y1 - $spaceTextY ; $orientation = "right" ; 
172                                        push @positions, [$x1, $x2, $y1, $y2, $orientation] ;
173                                        $x1 = $x - $sizeX1/2 - $spaceTextX ; $x2 = $x - $sizeX1/2 ; $y2 = $y - $sizeY1/2 ; $y1 = $y2 + $spaceTextY ; $orientation = "right" ; 
174                                        push @positions, [$x1, $x2, $y1, $y2, $orientation] ;
175
176                                        # left corners, bottom, top
177                                        $x1 = $x - $sizeX1/2 - $spaceTextX ; $x2 = $x - $sizeX1/2 ; $y2 = $y + $sizeY1/2 ; $y1 = $y2 + $spaceTextY ; $orientation = "right" ; 
178                                        push @positions, [$x1, $x2, $y1, $y2, $orientation] ;
179                                        $x1 = $x - $sizeX1/2 - $spaceTextX ; $x2 = $x - $sizeX1/2 ; $y1 = $y - $sizeY1/2 ; $y2 = $y1 - $spaceTextY ; $orientation = "right" ; 
180                                        push @positions, [$x1, $x2, $y1, $y2, $orientation] ;
181
182
183                                        $tries = 0 ;
184                                        LABB: foreach my $pos (@positions) {
185                                                $tries++ ;
186
187                                                $positionFound = checkAndDrawText ($pos->[0], $pos->[1], $pos->[2], $pos->[3], $pos->[4], \@lines, $svgText, $layer) ;
188
189                                                if ($positionFound == 1) {
190                                                        last LABB ;
191                                                }
192                                        }
193                                        if ($positionFound == 0) { $numLabelsOmitted++ ; }
194                                        if ($tries > 1) { $numLabelsMoved++ ; }
195                                }
196                        }
197                        else {
198                                # no, count omitted
199                                $numIconsOmitted++ ;
200                        }
201        }
202        else { # only text
203                my ($x1, $x2, $y1, $y2) ;
204                # x1, x2, y1, y2
205                # left, right, bottom, top             
206                # choose space for text, draw
207                # count omitted
208
209                $numLabels++ ;
210                my @positions = () ;
211                $x1 = $x + $thickness ; $x2 = $x + $thickness + $spaceTextX ; $y1 = $y ; $y2 = $y - $spaceTextY ; $orientation = "left" ; 
212                push @positions, [$x1, $x2, $y1, $y2, $orientation] ;
213                $x1 = $x + $thickness ; $x2 = $x + $thickness + $spaceTextX ; $y1 = $y + $spaceTextY ; $y2 = $y ; $orientation = "left" ; 
214                push @positions, [$x1, $x2, $y1, $y2, $orientation] ;
215
216                $x1 = $x - ($thickness + $spaceTextX) ; $x2 = $x - $thickness ; $y1 = $y ; $y2 = $y - $spaceTextY ; $orientation = "right" ; 
217                push @positions, [$x1, $x2, $y1, $y2, $orientation] ;
218                $x1 = $x - ($thickness + $spaceTextX) ; $x2 = $x - $thickness ; $y1 = $y ; $y2 = $y - $spaceTextY ; $orientation = "right" ; 
219                push @positions, [$x1, $x2, $y1, $y2, $orientation] ;
220
221                $x1 = $x - $spaceTextX/2 ; $x2 = $x + $spaceTextX/2 ; $y1 = $y - $thickness ; $y2 = $y - ($thickness + $spaceTextY) ; $orientation = "centered" ; 
222                push @positions, [$x1, $x2, $y1, $y2, $orientation] ;
223                $x1 = $x - $spaceTextX/2 ; $x2 = $x + $spaceTextX/2 ; $y1 = $y + $thickness + $spaceTextY ; $y2 = $y + $thickness ; $orientation = "centered" ; 
224                push @positions, [$x1, $x2, $y1, $y2, $orientation] ;
225
226                my $positionFound = 0 ;
227                $tries = 0 ;
228                LABA: foreach my $pos (@positions) {
229                        $tries++ ;
230                        # print "$lines[0]   $pos->[0], $pos->[1], $pos->[2], $pos->[3], $pos->[4], $numLines\n" ;
231
232                        $positionFound = checkAndDrawText ($pos->[0], $pos->[1], $pos->[2], $pos->[3], $pos->[4], \@lines, $svgText, $layer) ;
233
234                        if ($positionFound == 1) {
235                                last LABA ;
236                        }
237                }
238                if ($positionFound == 0) { $numLabelsOmitted++ ; }
239                if ($tries > 1) { $numLabelsMoved++ ; }
240        }
241}
242
243
244sub checkAndDrawText {
245#
246# checks if area available and if so draws text
247#
248        my ($x1, $x2, $y1, $y2, $orientation, $refLines, $svgText, $layer) = @_ ;
249
250        if (cv('debug') eq "1") { print "CADT: $x1, $x2, $y1, $y2, $orientation, $refLines, $svgText, $layer\n" ; }
251
252        my @lines = @$refLines ;
253        my $numLines = scalar @lines ;
254        my $lineDist = cv ('linedist') ;
255
256        my ($size) = ( $svgText =~ /font-size=\"(\d+)\"/ ) ;
257        if ( ! defined $size ) { die ("ERROR: font size could not be determined from svg format string \"$svgText\"\n") ; }
258
259        if ( ! areaOccupied ($x1, $x2, $y1, $y2)) {
260
261                for (my $i=0; $i<=$#lines; $i++) {
262
263                        my @points = ($x1, $y2+($i+1)*($size+$lineDist), $x2, $y2+($i+1)*($size+$lineDist)) ;
264                        my $pathName = "LabelPath" . $labelPathId ; 
265                        $labelPathId++ ;
266                        createPath ($pathName, \@points, "definitions") ;
267
268                        if ($orientation eq "centered") {
269                                pathText ($svgText, $lines[$i], $pathName, 0, "middle", 50, $layer)
270                        }
271                        if ($orientation eq "left") {
272                                pathText ($svgText, $lines[$i], $pathName, 0, "start", 0, $layer)
273                        }
274                        if ($orientation eq "right") {
275                                pathText ($svgText, $lines[$i], $pathName, 0, "end", 100, $layer)
276                        }
277                }
278
279                occupyArea ($x1, $x2, $y1, $y2) ;
280               
281                return (1) ;
282        }
283        else {
284                return 0 ;
285        }
286}
287
288sub splitLabel {
289#
290# split label text at space locations and then merge new parts if new part will be smaller than XX chars
291#
292        my $text = shift ;
293        my @lines = split / /, $text ;
294        my $merged = 1 ;
295        while ($merged) {
296                $merged = 0 ;
297                LAB2: for (my $i=0; $i<$#lines; $i++) {
298                        if (length ($lines[$i] . " " . $lines[$i+1]) <= cv ('maxcharperline') ) {       
299                                $lines[$i] = $lines[$i] . " " . $lines[$i+1] ;
300                                splice (@lines, $i+1, 1) ;
301                                $merged = 1 ;
302                                last LAB2 ;
303                        }
304                }
305        }
306        return (\@lines) ;
307}
308
309
310
311sub occupyArea {
312#
313# occupy area and make entry in quad tree for later use
314#
315        my ($x1, $x2, $y1, $y2) = @_ ;
316        # left, right, bottom, top (bottom > top!)
317        push @occupiedAreas, [$x1, $x2, $y1, $y2] ;
318        $qtPoiLabels->add ($#occupiedAreas, $x1, $y1, $x2, $y2) ;
319}
320
321sub areaOccupied {
322#
323# look up possible interfering objects in quad tree and check for collision
324#
325        my ($x1, $x2, $y1, $y2) = @_ ;
326        # left, right, bottom, top (bottom > top!)
327        my $occupied = 0 ;
328
329        my $ref2 = $qtPoiLabels->getEnclosedObjects ($x1, $y2, $x2, $y1) ;
330        my @index = @$ref2 ;
331        my @occupiedAreasTemp = () ;
332        foreach my $nr (@index) {
333                push @occupiedAreasTemp, $occupiedAreas[$nr] ;
334        } 
335
336        LAB1: foreach my $area (@occupiedAreasTemp) {
337                my $intersection = 1 ;
338                if ($x1 > $area->[1]) { $intersection = 0 ; } ;
339                if ($x2 < $area->[0]) { $intersection = 0 ; } ;
340                if ($y1 < $area->[3]) { $intersection = 0 ; } ;
341                if ($y2 > $area->[2]) { $intersection = 0 ; } ;
342                if ($intersection == 1) { 
343                        $occupied = 1 ; 
344                        last LAB1 ;     
345                }
346        }
347        return ($occupied) ;
348}
349
350sub lineCrossings {
351#
352# checks for line collisions
353# accepts multiple lines in form of multiple coordinates
354#
355        my ($ref) = shift ;
356        my @coordinates = @$ref ;
357        my @testLines = () ;
358
359        for (my $i=0; $i<$#coordinates-2; $i+=2) {
360                push @testLines, [$coordinates[$i], $coordinates[$i+1], $coordinates[$i+2], $coordinates[$i+3]] ;
361        }
362
363        # find area of way
364        my ($found) = 0 ;
365        my $xMin = 999999 ; my $xMax = 0 ;
366        my $yMin = 999999 ; my $yMax = 0 ;
367        foreach my $l1 (@testLines) {
368                if ($l1->[0] > $xMax) { $xMax = $l1->[0] ; }
369                if ($l1->[0] < $xMin) { $xMin = $l1->[0] ; }
370                if ($l1->[1] > $yMax) { $yMax = $l1->[1] ; }
371                if ($l1->[1] < $yMin) { $yMin = $l1->[1] ; }
372        }
373       
374        # get indexes from quad tree
375        my $ref2 = $qtWayLabels->getEnclosedObjects ($xMin, $yMin, $xMax, $yMax) ;
376        # create array linesInArea
377        my @linesInAreaIndex = @$ref2 ;
378        my @linesInArea = () ;
379        foreach my $lineNr (@linesInAreaIndex) {
380                push @linesInArea, $lines[$lineNr] ;
381        } 
382
383        LABCR: foreach my $l1 (@testLines) {
384                foreach my $l2 (@linesInArea) {
385                        my ($x, $y) = intersection (@$l1, @$l2) ;
386                        if (($x !=0) and ($y != 0)) {
387                                $found = 1 ;
388                                last LABCR ;
389                        }
390                }
391        }
392        if ($found == 0) {
393                return 0 ;
394        }
395        else {
396                return 1 ;
397        }       
398}
399
400
401sub occupyLines {
402#
403# store drawn lines and make quad tree entries
404# accepts multiple coordinates that form a way
405#
406        my ($ref) = shift ;
407        my @coordinates = @$ref ;
408
409        for (my $i=0; $i<$#coordinates-2; $i+=2) {
410                push @lines, [$coordinates[$i], $coordinates[$i+1], $coordinates[$i+2], $coordinates[$i+3]] ;
411                # print "PUSHED $coordinates[$i], $coordinates[$i+1], $coordinates[$i+2], $coordinates[$i+3]\n" ;
412                # drawWayPix ("black", 1, 0, @coordinates)
413
414                $qtWayLabels->add ($#lines, $coordinates[$i], $coordinates[$i+1], $coordinates[$i+2], $coordinates[$i+3]) ;
415
416        }
417}
418
419# ------------------------------------------------------------
420
421sub addToPoiHash {
422        my ($name, $sq) = @_ ;
423        if (defined $sq) {
424                $poiHash{$name}{$sq} = 1 ;
425        }
426        else {
427                $poiHash{$name} = 1 ;
428        }
429}
430
431
432sub getPoiHash {
433        return \%poiHash ;
434}
435
436
4371 ;
438
439
Note: See TracBrowser for help on using the repository browser.