# -*- perl -*- # # $Id: MapServer.pm,v 1.25 2005/11/16 01:47:43 eserte Exp $ # Author: Slaven Rezic # # Copyright (C) 2003 Slaven Rezic. All rights reserved. # This program is free software; you can redistribute it and/or # modify it under the same terms as Perl itself. # # Mail: slaven@rezic.de # WWW: http://bbbike.sourceforge.net/ # package BBBikeDraw::MapServer; use strict; use base qw(BBBikeDraw); use Strassen; # Strassen benutzt FindBin benutzt Carp, also brauchen wir hier nicht zu # sparen: use Carp qw(confess); use vars qw($VERSION $DEBUG %color %outline_color %width); $DEBUG = 0 if !defined $DEBUG; $VERSION = sprintf("%d.%02d", q$Revision: 1.25 $ =~ /(\d+)\.(\d+)/); { package BBBikeDraw::MapServer::Conf; use base qw(Class::Accessor); __PACKAGE__->mk_accessors(qw(BbbikeDir MapserverMapDir MapserverBinDir MapserverRelurl MapserverUrl TemplateMap ImageSuffix FontsList)); sub new { bless {}, shift } # XXX How to code the preferences better? sub vran_default { my $self = shift->new; my $HOME = "/home/e/eserte"; $self->BbbikeDir("$HOME/src/bbbike"); $self->MapserverMapDir($self->BbbikeDir . "/mapserver/brb"); if (0) { # 1 for current version from CVS $self->MapserverBinDir("/usr/local/src/work/mapserver"); } else { $self->MapserverBinDir("/usr/local/src/mapserver/mapserver-3.6.4"); } $self->MapserverRelurl("/~eserte/mapserver/brb"); $self->MapserverUrl("http://www/~eserte/mapserver/brb"); $self->TemplateMap("brb.map-tpl"); $self->ImageSuffix("png"); $self->FontsList("fonts-vran.list"); $self; } sub ipaq_vran_default { my $self = shift->new; my(%args) = @_; my $HOME = "/home/e/eserte"; $self->BbbikeDir("$HOME/src/bbbike"); $self->MapserverMapDir($self->BbbikeDir . "/mapserver/brb"); $self->MapserverBinDir("/usr/local/src/mapserver/mapserver-3.6.4"); $self->MapserverRelurl("/~eserte/mapserver/brb"); $self->MapserverUrl("http://www/~eserte/mapserver/brb"); $self->TemplateMap("brb-ipaq.map-tpl"); $self->ImageSuffix($args{ImageType} || "png"); $self->FontsList("fonts-vran.list"); $self; } sub radzeit_default { my $self = shift->new; my $apache_root; my $htdocs; my $fontslist; if (-d "/var/www/domains/radzeit.de/www/BBBike/data/") { # new radzeit.de $apache_root = "/var/www/domains/radzeit.de/www"; $htdocs = "public"; $fontslist = "fonts-radzeit.list"; } else { $apache_root = "/usr/local/apache/radzeit"; $htdocs = "htdocs"; $fontslist = "fonts-radzeit-old.list"; } $self->BbbikeDir("$apache_root/BBBike"); $self->MapserverMapDir("$apache_root/$htdocs/mapserver/brb"); $self->MapserverBinDir("$apache_root/cgi-bin"); $self->MapserverRelurl("/mapserver/brb"); $self->MapserverUrl("http://www.radzeit.de/mapserver/brb"); $self->TemplateMap("brb.map-tpl"); $self->ImageSuffix("png"); $self->FontsList($fontslist); $self; } sub radzeit_herceg_de_default { my $self = shift->new; my $apache_root = "/home/e/eserte/src/bbbike/projects/www.radzeit.de"; $self->BbbikeDir("$apache_root/BBBike"); if (-d "$apache_root/public/mapserver/brb") { # new radzeit.de $self->MapserverMapDir("$apache_root/public/mapserver/brb"); } else { $self->MapserverMapDir("$apache_root/htdocs/mapserver/brb"); } #$self->MapserverBinDir("$apache_root/cgi-bin"); $self->MapserverBinDir("/usr/local/src/mapserver/mapserver-3.6.4"); $self->MapserverRelurl("/mapserver/brb"); $self->MapserverUrl("http://radzeit.herceg.de/mapserver/brb"); $self->TemplateMap("brb.map-tpl"); $self->ImageSuffix("png"); #$self->FontsList("fonts-radzeit.list"); $self->FontsList("fonts-vran.list"); $self; } sub bbbike_cgi_conf { my $self = shift->new; my(%args) = @_[1..$#_]; # guess position of bbbike.cgi.config require File::Basename; require File::Spec; my $bbbike_dir = File::Spec->rel2abs(File::Basename::dirname(File::Basename::dirname($INC{"BBBikeDraw/MapServer.pm"}))); my $bbbike_cgi_conf_path = File::Spec->catfile($bbbike_dir, "cgi", "bbbike.cgi.config"); if (!-r $bbbike_cgi_conf_path) { die "$bbbike_cgi_conf_path is not existent or readable"; } require BBBikeMapserver; my $ms = BBBikeMapserver->new; $ms->read_config($bbbike_cgi_conf_path); $self->BbbikeDir("$bbbike_dir"); $self->MapserverMapDir($ms->{MAPSERVER_DIR}); if (!defined $ms->{MAPSERVER_BIN_DIR}) { die "Please define \$mapserver_bin_dir in $bbbike_cgi_conf_path"; } $self->MapserverBinDir($ms->{MAPSERVER_BIN_DIR}); $self->MapserverRelurl($ms->{MAPSERVER_PROG_RELURL}); $self->MapserverUrl($ms->{MAPSERVER_PROG_URL}); $self->TemplateMap("brb.map-tpl"); $self->ImageSuffix($args{ImageType} || "png"); if (!defined $ms->{MAPSERVER_FONTS_LIST}) { die "Please define \$mapserver_fonts_list in $bbbike_cgi_conf_path"; } $self->FontsList($ms->{MAPSERVER_FONTS_LIST}); $self; } sub bbbike_cgi_ipaq_conf { my $self = __PACKAGE__->bbbike_cgi_conf; my(%args) = @_[1..$#_]; $self->TemplateMap("brb-ipaq.map-tpl"); $self->ImageSuffix($args{ImageType} || "png"); $self; } } { package BBBikeDraw::MapServer::Image; use base qw(Class::Accessor); use vars qw(@accessors @computed_accessors); @accessors = qw(Width Height Imagecolor Transparent BBox ColorGreyBg ColorWhite ColorYellow ColorRed ColorGreen ColorMiddleGreen ColorDarkGreen ColorDarkBlue ColorLightBlue ColorRose ColorBlack OnFlaechen OnGewaesser OnStrassen OnUBahn OnSBahn OnRBahn OnAmpeln OnOrte OnFaehren OnGrenzen OnFragezeichen OnObst OnRoute OnStartFlag OnGoalFlag OnMarkerPoint OnTitle OnRadwege OnQualitaet OnHandicap OnBlocked OnMount StartFlagPoints GoalFlagPoints MarkerPoint TitleText RouteCoords MapserverDir MapserverRelurl MapserverUrl BbbikeDir ImageDir ImageSuffix FontsList ); @computed_accessors = qw(Conf ImageType); __PACKAGE__->mk_accessors(@accessors); sub ImageType { my $suffix = shift->ImageSuffix; uc($suffix); } sub Conf { my $self = shift; if (@_) { $self->set("Conf", @_); } else { my $conf = $self->get("Conf"); if (!$conf) { require Sys::Hostname; if (defined $ENV{SERVER_NAME} && $ENV{SERVER_NAME} =~ /radzeit\.de$/) { $conf = BBBikeDraw::MapServer::Conf->radzeit_default; } elsif (defined $ENV{SERVER_NAME} && $ENV{SERVER_NAME} =~ /radzeit\.herceg\.de$/) { $conf = BBBikeDraw::MapServer::Conf->radzeit_herceg_de_default; } elsif (Sys::Hostname::hostname() =~ /herceg\.de$/) { $conf = BBBikeDraw::MapServer::Conf->vran_default; } else { $conf = BBBikeDraw::MapServer::Conf->bbbike_cgi_conf; } } $conf; } } use Template; use File::Temp qw(tempfile); sub new { my($package, $w, $h) = @_; my $self = bless {}, $package; $self->Width($w); $self->Height($h); $self; } sub imageOut { my $self = shift; my $conf = $self->Conf; my($mapfh, $mapfile) = tempfile (UNLINK => !$BBBikeDraw::MapServer::DEBUG, SUFFIX => ".map"); $self->BbbikeDir($conf->BbbikeDir); $self->ImageDir($self->BbbikeDir . "/images"); my $mapserver_dir = $conf->MapserverMapDir; my $mapserver_bin_dir = $conf->MapserverBinDir; $self->MapserverDir($mapserver_dir); for my $var (qw(MapserverRelurl MapserverUrl ImageSuffix FontsList)) { $self->$var($conf->$var()); } my $t = Template->new(DEBUG => 0, # Can't use DEBUG=>1 with new TT ABSOLUTE => 1, INCLUDE_PATH => $mapserver_dir, ); my $vars = {}; foreach my $k (@accessors, @computed_accessors) { my $v = $self->$k(); (my $k2 = $k) =~ s/(?<=.)([A-Z])/_$1/g; $k2 = uc($k2); if ($k2 =~ /^(WIDTH|HEIGHT)$/) { $k2 = "IMG$k2"; } elsif ($k2 =~ /^COLOR_/ || $k2 eq 'IMAGECOLOR') { $v = join(" ", @$v); } if ($k2 =~ /^ON_/) { $v = ($v ? 'ON' : 'OFF'); } $vars->{$k2} = $v; } $t->process("$mapserver_dir/" . $conf->TemplateMap, $vars, $mapfh) || die $t->error; close $mapfh; my @cmd = ( #"valgrind", "$mapserver_bin_dir/shp2img", "-m", $mapfile, "-e", @{ $self->BBox }, ); #warn "@cmd"; my $buf; # if ($ENV{MOD_PERL}) { # my($s2i_fh, $s2i_filename) = tempfile(UNLINK => 1, # SUFFIX => ".img"); # push @cmd, "-o", $s2i_filename; # system(@cmd); # die "Command failed with $?: @cmd" if $?; # open(IMG, $s2i_filename) or die "Can't open $s2i_filename: $!"; # local $/ = undef; # $buf = ; # close IMG; # } else { open(SHP2IMG, "-|") or do { { exec @cmd; } die "Can't exec @cmd: $!"; }; local $/ = undef; $buf = ; close SHP2IMG; # } $buf; } } sub init { my $self = shift; $self->SUPER::init(); $self->{Width} ||= 640; $self->{Height} ||= 480; my $im; if ($self->{OldImage}) { die "No support for drawing over old images in " . __PACKAGE__; } else { $im = BBBikeDraw::MapServer::Image->new($self->{Width},$self->{Height}); } $self->{Image} = $im; $im->Conf($self->{Conf}); $self->allocate_colors; # $self->set_category_colors; # $self->set_category_outline_colors; # $self->set_category_widths; $self->set_draw_elements; $self; } sub allocate_colors { my $self = shift; my $im = $self->{Image}; $self->{'Bg'} = '' if !defined $self->{'Bg'}; if ($self->{'Bg'} =~ /^white/) { # Hintergrund weiß: Nebenstraßen werden grau, # Hauptstraßen dunkelgelb gezeichnet $im->ColorGreyBg([255,255,255]); $im->ColorWhite ([153,153,153]); $im->ColorYellow([180,180,0]); } elsif ($self->{'Bg'} =~ /^\#([a-f0-9]{2})([a-f0-9]{2})([a-f0-9]{2})/i) { my($r,$g,$b) = (hex($1), hex($2), hex($3)); $im->ColorGreyBg([$r,$g,$b]); } else { #$im->ColorGreyBg([153,153,153]); # zu dunkel #$im->ColorGreyBg([225,225,225]); # zu hell für den iPAQ $im->ColorGreyBg([180,180,180]); } $im->Transparent($im->ColorGreyBg) if ($self->{'Bg'} =~ /transparent$/); $im->Imagecolor($im->ColorGreyBg); $im->ColorWhite ([255,255,255]) if !defined $im->ColorWhite; $im->ColorYellow ([255,255,0]) if !defined $im->ColorYellow; $im->ColorRed ([255,0,0]); $im->ColorGreen ([180,255,180]); # light green $im->ColorDarkGreen ([0,128,0]); $im->ColorDarkBlue ([0,0,128]); $im->ColorLightBlue ([0xa0,0xa0,0xff]); $im->ColorMiddleGreen([0, 200, 0]); $im->ColorRose ([215, 184, 200]); $im->ColorBlack ([0, 0, 0]); } sub draw_map { my $self = shift; $self->pre_draw if !$self->{PreDrawCalled}; my $im = $self->{Image}; $im->BBox([$self->{Min_x}, $self->{Min_y}, $self->{Max_x}, $self->{Max_y}]); # I could also use the "-l" option of shp2img, but this works # on the "NAME" of a layer, not a "GROUP"... foreach (@{$self->{Draw}}) { if ($_ eq 'title') { # XXX never positively tested $im->OnTitle(1); $im->TitleText($self->make_default_title); } elsif (/^ampeln?$/) { $im->OnAmpeln(1); } elsif ($_ eq 'strname') { # always on! } elsif ($_ eq 'str') { $im->OnStrassen(1); } elsif ($_ eq 'wasser') { $im->OnGewaesser(1); } elsif ($_ eq 'flaechen') { $im->OnFlaechen(1); } elsif ($_ eq 'faehren') { $im->OnFaehren(1); } elsif ($_ eq 'ubahn') { $im->OnUBahn(1); } elsif (/^[usr]bahnname$/) { # ignore silently } elsif ($_ eq 'sbahn') { $im->OnSBahn(1); } elsif ($_ eq 'rbahn') { $im->OnRBahn(1); } elsif ($_ eq 'berlin') { $im->OnGrenzen(1); } elsif ($_ =~ /^orte?$/) { $im->OnOrte(1); } elsif ($_ eq 'fragezeichen') { $im->OnFragezeichen(1); } elsif ($_ eq 'obst') { $im->OnObst(1); } elsif ($_ eq 'radwege') { $im->OnRadwege(1); } elsif ($_ eq 'qualitaet') { $im->OnQualitaet(1); } elsif ($_ eq 'handicap') { $im->OnHandicap(1); } elsif ($_ eq 'blocked') { $im->OnBlocked(1); } elsif ($_ eq 'mount') { $im->OnMount(1); } else { warn "Ignored: $_"; } } } # Zeichnen des Maßstabs sub draw_scale { die "draw_scale: NYI"; # my $self = shift; # my $im = $self->{Image}; # my $transpose = $self->{Transpose}; # my $x_margin = 10; # my $y_margin = 10; # my $color = $black; # my $bar_width = 4; # my($x0,$y0) = $transpose->(0,0); # my($x1,$y1, $strecke, $strecke_label); # for $strecke (1000, 5000, 10000, 20000, 50000, 100000) { # ($x1,$y1) = $transpose->($strecke,0); # if ($x1-$x0 > 30) { # $strecke_label = $strecke/1000 . "km"; # last; # } # } # $im->line($self->{Width}-($x1-$x0)-$x_margin, # $self->{Height}-$y_margin, # $self->{Width}-$x_margin, # $self->{Height}-$y_margin, # $color); # $im->line($self->{Width}-($x1-$x0)-$x_margin, # $self->{Height}-$y_margin-$bar_width, # $self->{Width}-$x_margin, # $self->{Height}-$y_margin-$bar_width, # $color); # $im->filledRectangle # ($self->{Width}-($x1-$x0)/2-$x_margin, # $self->{Height}-$y_margin-$bar_width, # $self->{Width}-$x_margin, # $self->{Height}-$y_margin, # $color); # $im->line($self->{Width}-($x1-$x0)/2-$x_margin, # $self->{Height}-$y_margin, # $self->{Width}-($x1-$x0)/2-$x_margin, # $self->{Height}-$y_margin-$bar_width, # $color); # $im->line($self->{Width}-($x1-$x0)-$x_margin, # $self->{Height}-$y_margin+2, # $self->{Width}-($x1-$x0)-$x_margin, # $self->{Height}-$y_margin-$bar_width-2, # $color); # $im->line($self->{Width}-$x_margin, # $self->{Height}-$y_margin+2, # $self->{Width}-$x_margin, # $self->{Height}-$y_margin-$bar_width-2, # $color); # $im->string(&GD::Font::Small, # $self->{Width}-($x1-$x0)-$x_margin-3, # $self->{Height}-$y_margin-$bar_width-2-12, # "0", $color); # $im->string(&GD::Font::Small, # $self->{Width}-$x_margin+8-6*length($strecke_label), # $self->{Height}-$y_margin-$bar_width-2-12, # $strecke_label, $color); } sub draw_route { my $self = shift; my $im = $self->{Image}; $im->OnRoute(1); $im->OnStartFlag(1); $im->OnGoalFlag(1); my(@c1) = @{ $self->{C1} }; $im->RouteCoords(join " ", map { @$_ } @c1); $im->StartFlagPoints(join " ", @{ $c1[0] }); $im->GoalFlagPoints(join " ", @{ $c1[-1] }); if ($self->{MarkerPoint}) { $im->OnMarkerPoint(1); $im->MarkerPoint(join " ", split /,/, $self->{MarkerPoint}); } } sub draw_wind { # XXX use the TRANSFORM FALSE feature of layer objects in mapserver here! warn "draw_wind NYI"; # my $self = shift; # return unless $self->{Wind}; # require BBBikeCalc; # BBBikeCalc::init_wind(); # my $richtung = lc($self->{Wind}{Windrichtung}); # if ($richtung =~ /o$/) { $richtung =~ s/o$/e/; } # my $staerke = $self->{Wind}{Windstaerke}; # my $im = $self->{Image}; # my($radx, $rady) = (10, 10); # my $col = $darkblue; # $im->arc($self->{Width}-20, 20, $radx, $rady, 0, 360, $col); # $im->fill($self->{Width}-20, 20, $col); # if ($staerke > 0) { # my %senkrecht = # im Uhrzeigersinn # ('-1,-1' => [-1,+1], # '-1,0' => [ 0,+1], # '-1,1' => [+1,+1], # '0,1' => [+1, 0], # '1,1' => [+1,-1], # '1,0' => [ 0,-1], # '1,-1' => [-1,-1], # '0,-1' => [-1, 0], # ); # my($ydir, $xdir) = @{$BBBikeCalc::wind_dir{$richtung}}; # if (exists $senkrecht{"$xdir,$ydir"}) { # my($x2dir, $y2dir) = @{ $senkrecht{"$xdir,$ydir"} }; # my($yadd, $xadd) = map { -$_*15 } ($ydir, $xdir); # $xadd = -$xadd; # korrigieren # $im->line($self->{Width}-20, 20, $self->{Width}-20+$xadd, 20+$yadd, # $col); # my $this_tic = 15; # my $i = $staerke; # my $last_is_half = 0; # if ($i%2 == 1) { # $last_is_half++; # $i--; # } # while ($i >= 0) { # my($yadd, $xadd) = map { -$_*$this_tic } ($ydir, $xdir); # $xadd = -$xadd; # my $stroke_len; # if ($i == 0) { # if ($last_is_half) { # # half halbe Strichlänge # $stroke_len = 3; # } else { # last; # } # } else { # # full; volle Strichlänge # $stroke_len = 6; # } # my($yadd2, $xadd2) = map { -$_*$stroke_len } ($y2dir, $x2dir); # $xadd2 = -$xadd2; # $im->line($self->{Width}-20+$xadd, 20+$yadd, # $self->{Width}-20+$xadd+$xadd2, 20+$yadd+$yadd2, # $col); # $this_tic -= 3; # last if $this_tic <= 0; # $i-=2; # } # } # } } sub make_imagemap { require BBBikeDraw::GD; BBBikeDraw::GD::make_imagemap(@_); } sub flush { my $self = shift; my %args = @_; my $fh = $args{Fh} || $self->{Fh}; binmode $fh; print $fh $self->{Image}->imageOut; } sub empty_image_error { die "empty_image_error: NYI"; # my $self = shift; # my $im = $self->{Image}; # my $fh = $self->{Fh}; # $im->string(GD->gdLargeFont, 10, 10, "Empty image!", $white); # binmode $fh if $fh; # if ($fh) { # print $fh $im->imageOut; # } else { # print $im->imageOut; # } # confess "Empty image"; } 1;