source: subversion/applications/rendering/tahNG/development/ngcore.pl @ 26211

Last change on this file since 26211 was 7971, checked in by deelkar, 12 years ago

make option list shorter

  • Property svn:executable set to *
  • Property svn:keywords set to Revision
File size: 29.1 KB
Line 
1#!/usr/bin/perl -w
2#-----------------------------------------------------------------------------
3# OpenStreetMap tiles@home
4#
5# Contact Deelkar on the Openstreetmap wiki for help using this program
6#-----------------------------------------------------------------------------
7# Copyright 2006, Dirk-Lueder "Deelkar" Kreie
8#
9# This program is free software; you can redistribute it and/or
10# modify it under the terms of the GNU General Public License
11# as published by the Free Software Foundation; either version 2
12# of the License, or (at your option) any later version.
13#
14# This program is distributed in the hope that it will be useful,
15# but WITHOUT ANY WARRANTY; without even the implied warranty of
16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17# GNU General Public License for more details.
18#
19# You should have received a copy of the GNU General Public License
20# along with this program; if not, write to the Free Software
21# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
22#-----------------------------------------------------------------------------
23
24use strict;
25use GD qw(:DEFAULT :cmp);
26use Image::Magick;
27use LWP::UserAgent;
28use Math::Trig;
29use File::Copy;
30use File::Temp qw(tempfile);
31use AppConfig qw(:argcount);
32use FindBin qw($Bin);
33use English '-no_match_vars';
34use tahconfig;
35use tahlib;
36use tahproject;
37
38our $Config = AppConfig->new({
39                CREATE => 1,                      # Autocreate unknown config variables
40                GLOBAL => {
41                  DEFAULT  => undef,    # Create undefined Variables by default
42                  ARGCOUNT => ARGCOUNT_ONE, # Simple Values (no arrays, no hashmaps)
43                }
44              });
45
46$Config->define("help|usage!");
47$Config->define("nodownload=s");
48$Config->set("nodownload",0);
49$Config->file("config.defaults", "layers.conf", "authentication.conf", "tahng.conf"); #first read configs in order, each (possibly) overwriting settings from the previous
50$Config->args();              # overwrite config options with command line options
51$Config->file("config.svn");  # overwrite with hardcoded values that must not be changed
52ApplyConfigLogic($Config);
53my %EnvironmentInfo = CheckConfig($Config);
54
55my $Version = '$Revision: 7971 $';
56$Version =~ s/\$Revision:\s*(\d+)\s*\$/$1/;
57
58# Keep track of progress
59my ($progress,$progressJobs,$progressPercent,$currentSubTask) = (0,0,0,"none");
60
61# keep track of the server time for current job
62my $JobTime;
63
64# Subdirectory for the current job (layer & z12 tileset),
65# as used in sub GenerateTileset() and tileFilename()
66my $JobDirectory;
67
68#keep track of temporary files
69my @tempfiles;
70
71# We need to keep parent PID so that child get the correct files after fork()
72my $parent_pid = $PID;
73my $upload_pid = -1;
74
75my $Mode = shift();
76my $X = shift();
77my $Y = shift();
78die "Must specify tile coordinates\n" if( not defined $X or not defined $Y );
79my $Zoom = shift();
80if(not defined $Zoom)
81{
82    $Zoom = 12;
83    statusMessage(" *** No zoomlevel specified! Assuming z12 *** ", "warning", $progressJobs, $progressPercent,1);
84}
85
86$JobDirectory = $Config->get("WorkingDirectory");
87mkdir $JobDirectory unless -d $JobDirectory;
88my $TempDir = $Config{WorkingDirectory} . $PID . "/"; # avoid upload.pl looking at the wrong PNG (Regression caused by batik support)
89if (! -e $TempDir ) 
90{
91    mkdir($TempDir) or cleanUpAndDie("cannot create working directory $TempDir","EXIT",3,$PID);
92}
93elsif (! -d $TempDir )
94{
95    cleanUpAndDie("could not use $TempDir: is not a directory","EXIT",3,$PID);
96}
97
98if ($Mode eq "xy")
99{
100    GenerateTilesets($X, $Y, $Zoom);
101}
102
103#------------------------------------------------------
104# Download data
105#------------------------------------------------------
106sub downloadData
107{
108    my ($bbox,$bboxref,$DataFile,$URLS) = @_;
109   
110    killafile($DataFile);
111
112    my @tempfiles;
113    push(@tempfiles, $DataFile);
114    my $filelist = [];
115    my $i=0;
116    foreach my $URL (split(/ /,$URLS)) 
117    {
118        ++$i;
119        my $partialFile = $Config->get("WorkingDirectory").$PID."/data-$PID-$bboxref-$i.osm";
120        push(@{$filelist}, $partialFile);
121        push(@tempfiles, $partialFile);
122        statusMessage("Downloading: Map data for ".$Config->get("Layers")." to ".$partialFile, $currentSubTask, $progressJobs, $progressPercent,0);
123        print "Download $URL\n" if ($Config->get("Debug"));
124        DownloadFile($URL, $partialFile, 0);
125
126        if (-s $partialFile == 0)
127        {
128            statusMessage("No data here...", $currentSubTask, $progressJobs, $progressPercent, 1);
129            # if loop was requested just return  or else exit with an error.
130            # (to enable wrappers to better handle this situation
131            # i.e. tell the server the job hasn't been done yet)
132            PutRequestBackToServer($X,$Y,$Zoom,"NoData");
133            foreach my $file(@tempfiles) { killafile($file); }
134            addFault("nodataXAPI",1);
135            return cleanUpAndDie("GenerateTileset: no data!",$Mode,1,$PID),@tempfiles;
136        }
137        else
138        {
139            resetFault("nodataXAPI"); #reset to zero if data downloaded
140        }
141    }
142
143    mergeOsmFiles($DataFile, $filelist);
144    return 1,@tempfiles;
145}
146
147#-----------------------------------------------------------------------------
148# Render a tile (and all subtiles, down to a certain depth)
149#-----------------------------------------------------------------------------
150sub GenerateTilesets ## TODO: split some subprocesses to own subs
151{
152    my ($X, $Y, $Zoom) = @_;
153    $progress = 0;
154    $progressPercent = 0;
155    $progressJobs++;
156    $currentSubTask = "getdata";
157   
158    $JobDirectory = $Config->get("WorkingDirectory").$PID;
159    mkdir $JobDirectory unless -d $JobDirectory;
160
161    my $maxCoords = (2 ** $Zoom - 1);
162   
163    if ( ($X < 0) or ($X > $maxCoords) or ($Y < 0) or ($Y > $maxCoords) )
164    {
165        #maybe do something else here
166        die("\n Coordinates out of bounds (0..$maxCoords)\n");
167    }
168
169    my @lon;
170    my @lat;
171    my $Z=$Zoom+2;
172   
173    ($lon[0],$lon[1]) = ProjectL($X * 4, $Z);
174    ($lon[2],$lon[3]) = ProjectL($X * 4 + 2, $Z);
175    (undef,$lon[4]) = ProjectL($X, $Zoom);
176
177    ($lat[0],$lat[1]) = Project($Y * 4, $Z);
178    ($lat[2],$lat[3]) = Project($Y * 4 + 2, $Z);
179    (undef,$lat[4]) = Project($Y, $Zoom);
180
181    statusMessage(sprintf("Doing tileset $X,$Y (zoom $Zoom) (area around %f,%f)", $lat[2], $lon[2]), $currentSubTask, $progressJobs, $progressPercent, 1);
182
183    ## we now build our bboxes:
184
185    my $bboxRef;
186    my %bbox;
187    my $DataFile;
188
189    $bboxRef = sprintf("%d-AreaAndLabels",$Zoom); # get everything outside the tile area that might affect the tile itself, like areas and large labels.
190
191    my $N1 = $lat[0] + ($lat[0] - $lat[4]) * $Config->get("BorderN");
192    my $S1 = $lat[4] - ($lat[0] - $lat[4]) * $Config->get("BorderS");
193    my $E1 = $lon[4] + ($lon[4] - $lon[0]) * $Config->get("BorderE");
194    my $W1 = $lon[0] - ($lon[4] - $lon[0]) * $Config->get("BorderW");
195
196    # Adjust requested area to avoid boundary conditions
197    # TODO: verify the current system cannot handle segments/ways crossing the
198    # 180/-180 deg meridian and implement proper handling of this case, until
199    # then use this workaround:
200
201    if($W1 < -180) {
202      $W1 = -180; # api apparently can handle -180
203    }
204    if($E1 > 180) {
205      $E1 = 180;
206    }
207
208    $bbox{$bboxRef} = sprintf("%f,%f,%f,%f",
209      $W1, $S1, $E1, $N1);
210
211    my $status;
212    my $filelist = [];
213    my $URLS = sprintf("%s%s/*[bbox=%s]",
214         $Config->get("XAPIURL"), $Config->get("OSMVersion"), $bbox{$bboxRef});
215
216    my $rootDataFile = $Config->get("WorkingDirectory").$PID."/data-$bboxRef.osm"; ## FIXME broken TODO: make sure tempdir is created.
217    my $ppchainStart = $bboxRef; #the bit between data- and .osm is used as entry for the preprocessing chain later
218
219    if ($Config->get("nodownload"))
220    {
221        copy $Config->get("nodownload"), $rootDataFile or die "no such file ".$Config->get("nodownload");
222        # Get the server time for the data so we can assign it to the generated image (for tracking from when a tile actually is)
223        $JobTime = [stat $Config->get("nodownload")]->[9]; ## TODO: change this to use the XAPI timestamp which is a more accurate measure for from when the data is
224    }
225    else
226    {
227        ($status,@tempfiles) = downloadData($bbox{$bboxRef},$bboxRef,$rootDataFile,$URLS);
228        # Get the server time for the data so we can assign it to the generated image (for tracking from when a tile actually is)
229        $JobTime = [stat $rootDataFile]->[9]; ## TODO: change this to use the XAPI timestamp which is a more accurate measure for from when the data is
230    }
231
232     ## build the 16 zoom+2 (usually z14) bboxes
233     #  QR QC . SR SC: (QuadRow QuadColum SubRow SubColumn)
234     #
235     #  00.00  00.01  01.00  01.01
236     #
237     #  00.10  00.11  01.10  01.11
238     #
239     #  10.00  10.01  11.00  11.01
240     #
241     #  10.10  10.11  11.10  11.11
242
243     for (my $QR="0"; $QR le "1"; $QR++)
244     {
245        for (my $QC="0"; $QC le "1"; $QC++)
246        {
247            for (my $SR="0"; $SR le "1"; $SR++)
248            {
249                for (my $SC="0"; $SC le "1"; $SC++)
250                {
251                     my $N1 = $lat[$QR * 2 + $SR];
252                     my $S1 = $lat[$QR * 2 + $SR + 1];
253                     my $W1 = $lon[$QC * 2 + $SC];
254                     my $E1 = $lon[$QC * 2 + $SC + 1];
255                     $bboxRef = sprintf("%d-%d-%d", $Z, $QR*2 + $QC, $SR*2 + $SC); # ref: 14-0..3-0..3 will be used later in stitching.
256                     $bbox{$bboxRef} = sprintf("%f,%f,%f,%f", $W1, $S1, $E1, $N1);
257                }
258            }
259        }
260    }
261
262    # Check for correct UTF8 (else inkscape will run amok later)
263    # FIXME: This doesn't seem to catch all string errors that inkscape trips over.
264    statusMessage("Checking for UTF-8 errors in $rootDataFile", $currentSubTask, $progressJobs, $progressPercent, 0);
265    open(OSMDATA, $rootDataFile) or die ("could not open $rootDataFile for UTF-8 check");
266    my @toCheck = <OSMDATA>;
267    close(OSMDATA);
268    while (my $osmline = shift @toCheck)
269    {
270      if (utf8::is_utf8($osmline)) # this might require perl 5.8.1 or an explicit use statement
271      {
272        statusMessage("found incorrect UTF-8 chars in $DataFile, job $X $Y  $Zoom", $currentSubTask, $progressJobs, $progressPercent, 1);
273        PutRequestBackToServer($X,$Y,$Zoom,"BadUTF8");
274        addFault("utf8",1);
275        return cleanUpAndDie("GenerateTileset:UTF8 test failed",$Mode,1,$PID);
276      }
277    }
278    resetFault("utf8"); #reset to zero if no UTF8 errors found.
279
280    if ($Config->get("KeepDataFile"))
281    {
282        copy($rootDataFile, $Config->get("WorkingDirectory") . "/" . "data.osm");
283    }
284   
285    $currentSubTask = "Preproc";
286   
287    #------------------------------------------------------
288    # Handle all layers, one after the other
289    #------------------------------------------------------
290
291    foreach my $layer(split(/,/, $Config->get("Layers")))
292    {
293        #reset progress for each layer
294        $progress=0;
295        $progressPercent=0;
296        $currentSubTask = $layer;
297       
298        $JobDirectory = sprintf("%s%s_%d_%d_%d.tmpdir",
299                                $Config->get("WorkingDirectory").$PID."/",
300                                $Config->get($layer."_Prefix"),
301                                $Zoom, $X, $Y);
302        mkdir $JobDirectory unless -d $JobDirectory;
303
304        my $maxzoom = $Config->get($layer."_MaxZoom");
305       
306#        # Faff around
307#        for (my $i = $Zoom ; $i <= $maxzoom ; $i++)
308#        {
309#            killafile($Config->get("WorkingDirectory").$PID."/output-$parent_pid-z$i.svg");
310#        }
311       
312        my $Margin = " " x ($Zoom - 8);
313        #printf "%03d %s%d,%d: %1.2f - %1.2f, %1.2f - %1.2f\n", $Zoom, $Margin, $X, $Y, $S,$N, $W,$E;
314       
315        my $layerDataFile = PreProcess($ppchainStart, $layer, %bbox);
316        #------------------------------------------------------
317        # Preprocessing finished, start rendering
318        #------------------------------------------------------
319
320        # Add bounding box to osmarender
321        # then set the data source
322        # then transform it to SVG
323        # and immediately render it.
324
325        for (my $i = $Zoom ; $i <= $maxzoom and ($i < $Zoom + 2 or not $Config->get($layer."_Preprocessor") =~ /autocut/); $i++)
326        {
327            if (GenerateSVG($layerDataFile, $layer, $X, $Y, $i, $lat[0], $lat[4], $lon[0], $lon[4]))
328            {
329                foreach my $file(@tempfiles) { killafile($file) if (!$Config->get("Debug")); }
330                return 0;
331            }
332            else
333            {
334                my $SvgFile = $Config->get("WorkingDirectory").$PID."/output-$parent_pid-$X-$Y-z$i.svg";
335                # Find the size of the SVG file
336                my ($ImgH,$ImgW,$Valid) = getSize($SvgFile);
337                die "invalid coordinates" unless $Valid;
338
339                # Render the tile(s)
340                my ($success,$empty) = svg2png($SvgFile,$i,$Zoom,$layer,0,0,$ImgW,$ImgH,$X,$Y,
341                                       $Config->get("WorkingDirectory").$PID."/".$layer."_".$X."_".$Y."_".$Zoom.".tmpdir/"); ## FIXME: really necessary?
342
343                die "couldn't render png" unless $success;
344            }
345        }
346        for (my $i = $Zoom+2 ; $i <= $maxzoom and $Config->get($layer."_Preprocessor") =~ /autocut/; $i++)
347        {
348            for (my $ilat = 0 ; $ilat <= 3; $ilat++)
349            {
350                for (my $ilon = 0 ; $ilon <= 3; $ilon++)
351                {
352                    if (GenerateSVG($layerDataFile, $layer, $X*4+$ilon, $Y*4+$ilat, $i, $lat[$ilat], $lat[$ilat+1], $lon[$ilon], $lon[$ilon+1]))
353                    {
354                        foreach my $file(@tempfiles) { killafile($file) if (!$Config->get("Debug")); }
355                        return 0;
356                    }
357                }
358            }
359        }
360       
361       
362       
363
364        #### FIXME: my ($success,$empty) = RenderTile($layer, $X, $Y, $Y, $Zoom, $Zoom, $lat[0], $lat[4], $lon[0], $lon[4], 0,0,$ImgW,$ImgH,$ImgH,0);
365        if (!$success)
366        {
367            addFault("renderer",1);
368            return cleanUpAndDie("GenerateTileset: could not render tileset",$Mode,1,$PID);
369        }
370        else
371        {
372            resetFault("renderer");
373        }
374        # Clean-up the SVG files
375        for (my $i = $Zoom ; $i <= $maxzoom; $i++) 
376        {
377            killafile($Config->get("WorkingDirectory")."output-$parent_pid-z$i.svg") if (!$Config->get("Debug"));
378        }
379
380
381        #if $empty then the next zoom level was empty, so we only upload one tile unless RenderFullTileset is set.
382        if ($empty == 1 && $Config->get("GatherBlankTiles")) 
383        {
384            my $Filename=sprintf("%s_%s_%s_%s.png",$Config->get($layer."_Prefix"), $Zoom, $X, $Y);
385            my $oldFilename = sprintf("%s/%s",$JobDirectory, $Filename); 
386            my $newFilename = sprintf("%s%s",$Config->get("WorkingDirectory"),$Filename);
387            rename($oldFilename, $newFilename);
388            rmdir($JobDirectory);
389        }
390        else
391        {
392            # This directory is now ready for upload.
393            # How should errors in renaming be handled?
394            my $Dir = $JobDirectory;
395            $Dir =~ s|\.tmpdir|.dir|;
396            rename $JobDirectory, $Dir;
397        }
398
399        if ($Config->get("LayerUpload")) 
400        {
401            uploadIfEnoughTiles();
402        }
403    }
404
405    foreach my $file(@tempfiles) { killafile($file) if (!$Config->get("Debug")); }
406    return 1;
407}
408
409#------------------------------------------------------
410# Go through preprocessing steps for the current layer
411#------------------------------------------------------
412sub PreProcess
413{
414    my ($ppchainStart, $layer, %bbox) = @_;
415    my @ppchain = ($ppchainStart);
416    # config option may be empty, or a comma separated list of preprocessors
417    foreach my $preprocessor(split /,/, $Config->get($layer."_Preprocessor"))
418    {
419        my $inputFile = sprintf("%sdata-%s.osm", 
420            $Config->get("WorkingDirectory").$PID."/",
421            join("-", @ppchain));
422        push(@ppchain, $preprocessor);
423        my $outputFile = sprintf("%sdata-%s.osm", 
424            $Config->get("WorkingDirectory").$PID."/",
425            join("-", @ppchain));
426
427        if (-f $outputFile)
428        {
429            # no action; files for this preprocessing step seem to have been created
430            # by another layer already!
431        }
432        elsif ($preprocessor eq "maplint")
433        {
434            # Pre-process the data file using maplint
435            # TODO may put this into a subroutine of its own
436            my $Cmd = sprintf("%s \"%s\" tr %s %s > \"%s\"",
437                    $Config->get("Niceness"),
438                    $Config->get("XmlStarlet"),
439                    "maplint/lib/run-tests.xsl",
440                    "$inputFile",
441                    "tmp.$PID");
442            statusMessage("Running maplint", $currentSubTask, $progressJobs, $progressPercent,0);
443            runCommand($Cmd,$PID);
444            $Cmd = sprintf("%s \"%s\" tr %s %s > \"%s\"",
445                    $Config->get("Niceness"),
446                    $Config->get("XmlStarlet"),
447                    "maplint/lib/convert-to-tags.xsl",
448                    "tmp.$PID",
449                    "$outputFile");
450            statusMessage("Creating tags from maplint", $currentSubTask, $progressJobs, $progressPercent,0);
451            runCommand($Cmd,$PID);
452            killafile("tmp.$PID");
453        }
454        elsif ($preprocessor eq "close-areas")
455        {
456            my $Cmd = sprintf("%s perl close-areas.pl $X $Y $Zoom < %s > %s",
457                    $Config->get("Niceness"),
458                    "$inputFile",
459                    "$outputFile");
460            statusMessage("Running close-areas", $currentSubTask, $progressJobs, $progressPercent,0);
461            runCommand($Cmd,$PID);
462        }
463        elsif ($preprocessor eq "attribution")
464        {
465            my $Cmd = sprintf("%s perl attribution.pl < %s > %s",
466                    $Config->get("Niceness"),
467                    "$inputFile",
468                    "$outputFile");
469            statusMessage("Running attribution", $currentSubTask, $progressJobs, $progressPercent,0);
470            runCommand($Cmd,$PID);
471        }
472        elsif ($preprocessor eq "autocut")
473        {
474            statusMessage("Running autocut", $currentSubTask, $progressJobs, $progressPercent,0);
475            my $autocutJobs = scalar(keys %bbox);
476            my $progressAutocut = 0;
477            my $DataFile;
478            foreach my $bboxRef (sort (keys %bbox)) 
479            {
480                print $bboxRef.": ".$bbox{$bboxRef}."\n" if $Config->get("Debug");
481                if ($bboxRef =~ m/AreaAndLabels/)
482                {
483                    $DataFile = $outputFile; 
484                }
485                else
486                {
487                    $DataFile = $Config->get("WorkingDirectory").$PID."/data-$bboxRef.osm";
488                    push(@tempfiles, $DataFile);
489                }
490                statusMessage("processing ".$bboxRef,"autocut", $progressAutocut, $progressPercent,0);
491                statusMessage("cropping $bbox{$bboxRef} from $inputFile to $DataFile","autocut", $progressAutocut, $progressPercent,1) if $Config->get("Verbose");
492                cropDataToBBox(split(/,/,$bbox{$bboxRef}), $inputFile, $DataFile); 
493                $progressAutocut++;
494                $progressPercent=100*$progressAutocut/$autocutJobs;
495                doneMessage("");
496            }
497            statusMessage("complete","autocut", $progressAutocut, $progressPercent,0);
498            $progressPercent=0;
499        }
500        else
501        {
502            die "I have no preprocessor called '$preprocessor'";
503        }
504        push(@tempfiles, $outputFile);
505    }
506
507    my $layerDataFile = sprintf("data-%s.osm", join("-", @ppchain)); # Don't put working directory here, the path is relative to the rulesfile
508    return $layerDataFile;
509}
510
511#-----------------------------------------------------------------------------
512# Generate SVG for one zoom level
513#   $layerDataFile - name of the OSM data file
514#   $X, $Y - which tileset (Always the tilenumbers of the base zoom. i.e. z12)
515#   $Zoom - which zoom currently is processsed
516#   $N, $S, $W, $E - bounds of the tile
517#-----------------------------------------------------------------------------
518sub GenerateSVG 
519{
520    my ($layerDataFile, $layer, $X, $Y, $Zoom, $N, $S, $W, $E) = @_;
521    # Create a new copy of rules file to allow background update
522    # don't need layer in name of file as we'll
523    # process one layer after the other
524    my $error = 0;
525    my $source = $Config->get($layer."_Rules.$Zoom");
526    my $TempFeatures = $Config->get("WorkingDirectory").$PID."/map-features-$PID-$X-$Y-z$Zoom.xml";
527    copy($source, $TempFeatures)
528        or die "Cannot make copy of $source";
529
530    # Update the rules file  with details of what to do (where to get data, what bounds to use)
531    AddBounds($TempFeatures,$W,$S,$E,$N);
532    SetDataSource($layerDataFile, $TempFeatures);
533
534    # Render the file
535    if (! xml2svg(
536            $TempFeatures,
537            $Config->get("WorkingDirectory").$PID."/output-$parent_pid-$X-$Y-z$Zoom.svg",
538            $Zoom))
539    {
540        $error = 1;
541        print "some error occured in xml2svg \n" if ($Config->get("Debug"));
542    }
543    # Delete temporary rules file
544    killafile($TempFeatures) if (! $Config->get("Debug"));
545    return $error;
546}
547
548#-----------------------------------------------------------------------------
549# Add bounding-box information to an osm-map-features file
550#-----------------------------------------------------------------------------
551sub AddBounds 
552{
553    my ($Filename,$W,$S,$E,$N,$Size) = @_;
554   
555    # Read the old file
556    open(my $fpIn, "<", "$Filename");
557    my $Data = join("",<$fpIn>);
558    close $fpIn;
559    die("no such $Filename") if(! -f $Filename);
560   
561    # Change some stuff
562    my $BoundsInfo = sprintf(
563      "<bounds minlat=\"%f\" minlon=\"%f\" maxlat=\"%f\" maxlon=\"%f\" />",
564      $S, $W, $N, $E);
565   
566    $Data =~ s/(<!--bounds_mkr1-->).*(<!--bounds_mkr2-->)/$1\n<!-- Inserted by tilesGen -->\n$BoundsInfo\n$2/s;
567   
568    # Save back to the same location
569    open(my $fpOut, ">$Filename");
570    print $fpOut $Data;
571    close $fpOut;
572}
573
574#-----------------------------------------------------------------------------
575# Set data source file name in map-features file
576#-----------------------------------------------------------------------------
577sub SetDataSource 
578{
579    my ($Datafile, $Rulesfile) = @_;
580
581    # Read the old file
582    open(my $fpIn, "<", "$Rulesfile");
583    my $Data = join("",<$fpIn>);
584    close $fpIn;
585    die("no such $Rulesfile") if(! -f $Rulesfile);
586
587    $Data =~ s/(  data=\").*(  scale=\")/$1$Datafile\"\n$2/s;
588
589    # Save back to the same location
590    open(my $fpOut, ">$Rulesfile");
591    print $fpOut $Data;
592    close $fpOut;
593}
594
595#-----------------------------------------------------------------------------
596# Get the width and height (in SVG units, must be pixels) of an SVG file
597#-----------------------------------------------------------------------------
598sub getSize($)
599{
600    my $SVG = shift();
601    my ($width, $height);
602    open(my $fpSvg,"<",$SVG);
603    while(my $Line = <$fpSvg>)
604    {
605        if($Line =~ /height=\"(.*)px\" width=\"(.*)px\"/)
606        {
607            ($width, $height)=($2,$1);
608            last;
609        }
610    }
611    close $fpSvg;
612    return($height,$width,1) if ($width and $height);
613    print "$SVG has no width and height info";
614    return((0,0,0));
615}
616
617#-----------------------------------------------------------------------------
618# Transform an OSM file (using osmarender) into SVG
619#-----------------------------------------------------------------------------
620sub xml2svg 
621{
622    my($MapFeatures, $SVG, $zoom) = @_;
623    my $TSVG = "$SVG";
624    my $NoBezier = $Config->get("NoBezier") || $zoom <= 11;
625
626    if (!$NoBezier) 
627    {
628        $TSVG = "$SVG-temp.svg";
629    }
630
631    if ($Config->get("Osmarender") eq "XSLT")
632    {
633        my $XslFile;
634
635        $XslFile = "osmarender/osmarender.xsl";
636
637        my $Cmd = sprintf("%s \"%s\" tr --maxdepth %s %s %s > \"%s\"",
638          $Config->get("Niceness"),
639          $Config->get("XmlStarlet"),
640          $Config->get("XmlStarletMaxDepth"),
641          $XslFile,
642          "$MapFeatures",
643          $TSVG);
644
645        statusMessage("Transforming zoom level $zoom with XSLT", $currentSubTask, $progressJobs, $progressPercent,0);
646        runCommand($Cmd,$PID);
647    }
648    elsif($Config->get("Osmarender") eq "orp")
649    {
650        chdir "orp";
651        my $Cmd = sprintf("%s perl orp.pl -r %s -o %s",
652          $Config->get("Niceness"),
653          $MapFeatures,
654          $TSVG);
655
656        statusMessage("Transforming zoom level $zoom with or/p", $currentSubTask, $progressJobs, $progressPercent,0);
657        runCommand($Cmd,$PID);
658        chdir "..";
659    }
660    else
661    {
662        die "invalid Osmarender setting in config";
663    }
664
665    # look at temporary svg wether it really is a svg or just the
666    # xmlstarlet dump and exit if the latter.
667    open(SVGTEST, "<", $TSVG) || return cleanUpAndDie("xml2svg failed to open svg",$Mode,3,$PID);
668    my $TestLine = <SVGTEST>;
669    chomp $TestLine;
670    close SVGTEST;
671
672    if (grep(!/</, $TestLine))
673    {
674       statusMessage("File $TSVG doesn't look like svg, aborting render.", $currentSubTask, $progressJobs, $progressPercent,1);
675       return cleanUpAndDie("xml2svg failed",$Mode,3,$PID);
676    }
677#-----------------------------------------------------------------------------
678# Process lines to Bezier curve hinting
679#-----------------------------------------------------------------------------
680    if (!$NoBezier) 
681    {   # do bezier curve hinting
682        my $Cmd = sprintf("%s perl ./lines2curves.pl %s > %s",
683          $Config->get("Niceness"),
684          $TSVG,
685          $SVG);
686        statusMessage("Beziercurvehinting zoom level $zoom", $currentSubTask, $progressJobs, $progressPercent,0);
687        runCommand($Cmd,$PID);
688#-----------------------------------------------------------------------------
689# Sanitycheck for Bezier curve hinting, no output = bezier curve hinting failed
690#-----------------------------------------------------------------------------
691        my $filesize= -s $SVG;
692        if (!$filesize) 
693        {
694            copy($TSVG,$SVG);
695            statusMessage("Error on Bezier Curve hinting, rendering without bezier curves", $currentSubTask, $progressJobs, $progressPercent,0);
696        }
697        killafile($TSVG) if (!$Config->get("Debug"));
698    }
699    else
700    {   # don't do bezier curve hinting
701        statusMessage("Bezier Curve hinting disabled.", $currentSubTask, $progressJobs, $progressPercent,0);
702    }
703    return 1;
704}
705
706
707#-----------------------------------------------------------------------------
708# Render a SVG file
709# $ZOrig - the lowest zoom level of the tileset
710# $X, $Y - tilemnumbers of the tileset
711# SizeX,Y - png size (i.e. 256x256)
712#-----------------------------------------------------------------------------
713sub svg2png
714{
715    my($svgFile, $Zoom, $ZOrig, $layer, $X1, $Y1, $X2, $Y2, $X, $Y, $OutputDir) = @_;
716    my $SizeX = 256 * 2**($Zoom - $ZOrig);
717    my $SizeY = $SizeX;
718   
719    my $TempFile;
720    my $stdOut;
721    my $TempDir = $Config->get("WorkingDirectory") . $parent_pid . "/";
722    (undef, $TempFile) = tempfile($layer."_".$PID."_part-XXXXXX", DIR => $TempDir, SUFFIX => ".png", OPEN => 0);
723    (undef, $stdOut) = tempfile("$PID-XXXXXX", DIR => $Config->get("WorkingDirectory"), SUFFIX => ".stdout", OPEN => 0);
724
725   
726    my $Cmd = "";
727   
728    my $Left = $X1;
729    my $Top = $Y1;
730    my $Width = $X2 - $X1;
731    my $Height = $Y2 - $Y1;
732   
733    if ($Config->get("Batik") == "1") # batik as jar
734    {
735        $Cmd = sprintf("%s%s java -Xms256M -Xmx%s -jar %s -w %d -h %d -a %f,%f,%f,%f -m image/png -d \"%s\" \"%s%s\" > %s", 
736        $Config->get("i18n") ? "LC_ALL=C " : "",
737        $Config->get("Niceness"),
738        $Config->get("BatikJVMSize"),
739        $Config->get("BatikPath"),
740        $SizeX,
741        $SizeY,
742        $Left,$Top,$Width,$Height,
743        $TempFile,
744        $OutputDir,
745        $svgFile,
746        $stdOut);
747    }
748    elsif ($Config->get("Batik") == "2") # batik as executable (wrapper of some sort, i.e. on gentoo)
749    {
750        $Cmd = sprintf("%s%s %s -w %d -h %d -a %f,%f,%f,%f -m image/png -d \"%s\" \"%s%s\" > %s",
751        $Config->get("i18n") ? "LC_ALL=C " : "",
752        $Config->get("Niceness"),
753        $Config->get("BatikPath"),
754        $SizeX,
755        $SizeY,
756        $Left,$Top,$Width,$Height,
757        $TempFile,
758        $OutputDir,
759        $svgFile,
760        $stdOut);
761    }
762    else
763    {
764        $Cmd = sprintf("%s%s \"%s\" -z -w %d -h %d --export-area=%f:%f:%f:%f --export-png=\"%s\" \"%s%s\" > %s", 
765        $Config->get("i18n") ? "LC_ALL=C " : "",
766        $Config->get("Niceness"),
767        $Config->get("Inkscape"),
768        $SizeX,
769        $SizeY,
770        $X1,$Y1,$X2,$Y2,
771        $TempFile,
772        $OutputDir,
773        $svgFile,
774        $stdOut);
775    }
776   
777    # stop rendering the current job when inkscape fails
778    statusMessage("Rendering", $currentSubTask, $progressJobs, $progressPercent,0);
779    print STDERR "\n$Cmd\n" if ($Config->get("Debug"));
780    if (not runCommand($Cmd,$PID) or ! -e $TempFile )
781    {
782        statusMessage("$Cmd failed", $currentSubTask, $progressJobs, $progressPercent, 1);
783        ## TODO: check this actually gets the correct coords
784        PutRequestBackToServer($X,$Y,$ZOrig,"BadSVG");
785        addFault("inkscape",1);
786        cleanUpAndDie("svg2png failed",$Mode,3,$PID);
787        return 0;
788    }
789    resetFault("inkscape"); # reset to zero if inkscape succeeds at least once
790    killafile($stdOut) if (not $Config->get("Debug"));
791    return 1; #return true if success
792}
793
Note: See TracBrowser for help on using the repository browser.