#!/usr/bin/perl -w # # drawmap - spot the monk! # Briac 'OeufMayo' Pilpré # 2001/06/15 # munged by Max 'Corion' Maischein # 2001/06/17 # Great earth maps available from http://apollo.spaceports.com/~jhasting/earth.html # A jcwren's monkmap compliant map can be found at # http://www.pilpre.com/briac/small_earth.jpg # jcwren's monks.xml file available at: # http://www.tinymicros.com/pm/monks.xml #drawmap.pl -i=northam10k.jpg -o=monkmap_northam.jpg -x=-1280 -y=-896 -m ./monks.xml -d cross.png -w 10800 -h 5400 #drawmap.pl -i=europe10k.jpg -o=monkmap_europe.jpg -x=-4880 -y=-695 -m ./monks.xml -d cross.png -w 10800 -h 5400 -H monkmap_europe.html #drawmap.pl -i=small_earth.jpg -o=monkmap_world.jpg -x=-25 -y=-3 -m ./monks.xml -d cross.png -C use strict; use lib 'lib/'; use vars qw($VERSION); $VERSION = 0.04; use XML::Simple; use Getopt::Mixed 'nextOption'; use GD; Getopt::Mixed::init('C:i i=s o=s d=s w=i h=i x:i y:i m=s H=s xml>m dot>d width>w height>h input>i output>o offsetx>x offsety>y nocaption>C html>H'); # Fetch the command line parameters my ($input, $output, $offsetx, $offsety, $dot, $xml, $width, $height, $nocaption, $html); while (my ($option, $value, $pretty) = nextOption()) { $input = $value if $option eq 'i'; $output = $value if $option eq 'o'; $offsetx = $value if $option eq 'x'; $offsety = $value if $option eq 'y'; $xml = $value if $option eq 'm'; $dot = $value if $option eq 'd'; $width = $value if $option eq 'w'; $height = $value if $option eq 'h'; $nocaption = 1 if $option eq 'C'; if ($option eq 'H') { $html = $value; $nocaption = 0; }; } Getopt::Mixed::cleanup(); &usage unless ($input && $output); $offsetx ||= 0; $offsety ||= 0; my (%monks, %href); # Parse the monks coordinates XML file I fetched from jcwren's stats site. # ( code to fetch & create the XML is available on request ) my $xs = new XML::Simple(); my $ref = $xs->XMLin($xml); # Fill the monks hash with their respective locations foreach (keys %{$ref->{monk}}){ push (@{$monks{$_}}, ( $ref->{monk}->{$_}->{location}->{latitude}, $ref->{monk}->{$_}->{location}->{longitude}, )); } # Load the pictures we need. my $map = GD::Image->newFromJpeg($input); my $flag = GD::Image->newFromPng($dot); my $white = $map->colorResolve(255,255,255); my $black = $map->colorResolve(0,0,0); unless ($width && $height){ ($width, $height) = $map->getBounds(); } my %points; #keep track of the points for managing monks density my %boxes; #keep track of the caption bounding boxes # First, lets filter out all monks not on the map : my ($img_width,$img_height) = $map->getBounds(); my @monks_off_map = grep { my ($x,$y) = coord2pix($monks{$_}->[0],$monks{$_}->[1], $width, $height); # Tweak the x/y to fit the picture $x += $offsetx; $y += $offsety; # We want only the off-map monks : ! (between(0,$width-$offsetx, $x) && between(0,$height-$offsety,$y)) || ! (between(0,$img_width, $x) && between(0,$img_height,$y)); } keys %monks; foreach (@monks_off_map) { delete $monks{$_}; }; # Now, we want to place all position markers : my $f = 6; # "closeness" factor foreach (keys %monks){ # Convert the lat/long to x/y my ($x,$y) = coord2pix($monks{$_}->[0],$monks{$_}->[1], $width, $height); # Tweak the x/y to fit the picture $x += $offsetx; $y += $offsety; $points{$_} = [$x-$f, $y-$f, $x+$f, $y+$f]; # store the current pos $boxes{"__$_"} = [$x-$f, $y-$f, $x+$f, $y+$f]; # store the current pos of the bbox # Pinpoints the monk location on the map $map->copy($flag, $x, $y, 0,0,7,7); }; foreach (keys %monks){ # Convert the lat/long to x/y my ($x,$y) = coord2pix($monks{$_}->[0],$monks{$_}->[1], $width, $height); # Tweak the x/y to fit the picture $x += $offsetx; $y += $offsety; # Let's find if we have a monk close to the current one unless ($nocaption){ my ($x1,$y1); my ($radius, $angle) = (10,0); my $textl = 7 * length($_); #length of the caption # Create a box for the label my @box = (int($x-$textl/2), $y-17, int($x+$textl/2), $y-18+13); if (find_density(\%points, $_, $x,$y) || # If true the monk is too close find_intersect(\%boxes, $_, @box) # or the place has been taken already ) { CLOSE :{ $radius += 5; $angle += 10 % 360; # Find a point on a circle. # provided by CheeseLord: (x+r cos a, y+r sin a) ($x1,$y1)=(int($x + ($radius * cos $angle)), int($y+($radius * sin $angle))); # Move the label @box = (int($x1-$textl/2), $y1, int($x1+$textl/2), $y1+13); # Check to see if it intersects with a previous caption redo CLOSE if find_intersect(\%boxes, $_, @box); $map->line($x+4, $y+4, $x1+4, $y1+4, $white); $map->string(gdMediumBoldFont, $x1 - $textl/2 + 2, $y1, $_, $black); $map->string(gdMediumBoldFont, $x1 - $textl/2 + 3, $y1, $_, $white); } } else { $map->string(gdMediumBoldFont, int($x - $textl/2)+1, $y-17, $_, $black); $map->string(gdMediumBoldFont, int($x - $textl/2), $y-18, $_, $white); } $boxes{$_} = [@box]; } } # We now save our masterpiece on a storage device open JPGOUT, "> $output" or die $!; binmode JPGOUT; print JPGOUT $map->jpeg(75); if ($html){ open HTML, "> $html" or die $!; print HTML <<"__HTML__";