#!/usr/bin/perl -w # # MapServer package using MapQuest. # # Note that the scale on mapquest maps actually lies; it's based on # what you ask for, not what you get. Or at least that seems to be my # experience while playing with Irish maps, anyway. # Waider 2001 # August 2002: URLs and such appear to have changed, coping. # hmm. does this work? # http://www.mapquest.com/maps/map.adp?latlongtype=decimal&latitude=37.26617&longitude=-121.85858 # it does! package MapServer::MapQuest; use MapServer; our @ISA = "MapServer"; use LWP::UserAgent; use URI::Escape; use HTTP::Cookies; use WWW::Mechanize; # Grab a map from mapquest at a given lat/long my $MAPCACHE = "mq-cache"; # Scaling factors to get a pixel::gridpoint mapping in Tk. # # @ scale 8: # x: 181px = 0.01 # y: 233px = 0.01 my @xscale = ( 6000, 6000, 6000, 6000, 6000, 6000, 6000, 6000, 18100, 6000, ); # why are these negative? what drugs was I on? my @yscale = ( -6500, -6500, -6500, -6500, -6500, -6500, -6500, -6500, -23300, -6500, ); # Magnification. my $MAG = 8; # There seem to be a few magnification arrays, damn them. my %mag = ( '@' => { '9' => '1', # street '8' => 'y', '7' => 't', '6' => 'z', '5' => '8', '4' => 'r', '3' => 'b', '2' => 'a', '1' => '2', '0' => 'c', # country }, 'whatever' => { '9' => 'f', # '270ft', '8' => 'r', # '600ft', '7' => 'z', # '1500ft', '6' => 'y', # '1mi', '5' => '4', # '3mi', '4' => 'w', # '10mi', '3' => 'a', # '30mi', '2' => '0', # '80mi', '1' => 'u', # '300mi', '0' => 'b', # 'unscaled', }, ); my $ua; # FIXME this should be an option my $debug = 1; # Methods, madness, etc. sub mag() { my $obj = shift; $MAG = shift if @_; $MAG; } sub xscale() { my $obj = shift; return $xscale[ $MAG ]; } sub yscale() { my $obj = shift; return $yscale[ $MAG ]; } sub xfringe() { my $obj = shift; return 0.01; } sub yfringe() { my $obj = shift; return 0.01; } sub fetchmap( @ ) { my $self = shift; my ( $latitude, $longitude ) = @_; my $filename; my $inputs; my $mag = $MAG; # round off the lat/long $latitude = sprintf( "%.2f", $latitude ); $longitude = sprintf( "%.2f", $longitude ); -d $MAPCACHE || mkdir $MAPCACHE, 0755; -d "$MAPCACHE/$MAG" || mkdir "$MAPCACHE/$MAG", 0755; $filename = "cache_$ {latitude}_$ {longitude}.gif"; # First, check if we have a map that covers this area # Fixme - should go through various magnifications opendir( CACHEDIR, "$MAPCACHE/$MAG" ); my @files = grep /^cache/, readdir( CACHEDIR ); closedir( CACHEDIR ); for my $f ( @files ) { my ( $lat, $long ) = $f =~ m/cache_(.*?)_(.*?)\.gif$/; if (( $latitude > ( $lat - $self->xfringe())) && ( $latitude < ( $lat + $self->yfringe())) && ( $longitude > ( $long - $self->xfringe())) && ( $longitude < ( $long + $self->yfringe()))) { return "$MAPCACHE/$MAG/$f"; } } # There should be a range of options here. It'd be nice to know # we're peripherally on a map, especially if we're offline and can't # download a 'local' one. The 4 is arbitrary, and should really be # derived from $MAG or something. if ( !$self->online ) { for my $f ( @files ) { my ( $lat, $long ) = $f =~ m/cache_(.*?)_(.*?)\.gif$/; if (( $latitude > ( $lat - $self->xfringe() * 4 )) && ( $latitude < ( $lat + $self->yfringe() * 4 )) && ( $longitude > ( $long - $self->xfringe() * 4 )) && ( $longitude < ( $long + $self->yfringe() * 4 ))) { return "$MAPCACHE/$MAG/$f"; } } } # Only fetch an image if we're online. return undef unless $self->online(); print "Fetching a map from the server\n" if $debug; if ( !defined( $ua )) { $ua = new WWW::Mechanize( env_proxy => 1, autocheck => 1 ); $ua->agent_alias( 'Windows IE 6' ); } $filename = "$MAPCACHE/$MAG/$filename"; my $res = $ua->get( 'http://www.mapquest.com/maps/map.adp?latlongtype=decimal&latitude=' . $latitude . '&longitude=' . $longitude . '&zoom=' . $mag ); if ( !$res->is_success ) { if ( $res->as_string =~ /connection reset by peer/i ) { return undef; } print $res->as_string if $debug; return undef; } my $page = $res->content; my $parser = new HTML::TokeParser \$page; while ( my $tag = $parser->get_tag( "input" )) { next unless $tag->[1]{type}; next unless $tag->[1]{type} eq "image"; next unless $tag->[1]{name}; next unless $tag->[1]{name} eq "mqmap"; $url = $tag->[1]{src}; last; } if ( !$url) { print STDERR "No Image found in retrieved data\n"; print STDERR "=" x 79; print STDERR "\n"; print STDERR $page if $debug; return undef; } else { print "Image: " . $url . "\n" if $debug; } my $spang; ( $url, $spang ) = $url =~ /^(.*MQMapGenRequest=)(.*)$/i; $spang = uri_unescape( $spang ); # Bang the magnification up my $magrange = 'whatever'; if ( substr( $spang, 59, 1 ) eq '@' ) { $magrange = '@'; } # This is just pure abuse, really. #substr( $spang, 59 + ( $magrange ne 'whatever' ), 1 ) = #$mag{$magrange}->{$MAG}; # I don't recall writing this. It's evidently magic for frobbing # the image size. substr( $spang, 61 + ( $magrange ne 'whatever' ), 7 ) = '5hf$su6'; # This basically stops the little star icon from appearing if ( substr( $spang, 84 + ( $magrange ne 'whatever' ), 1 ) eq 'a' ) { substr( $spang, 84 + ( $magrange ne 'whatever' ), 1 ) = 'b'; } else { substr( $spang, 84 + ( $magrange ne 'whatever' ), 1 ) = 'a'; } $spang = uri_escape( $spang, "^A-Za-z0-9\-_.!~*',:" ); $url .= $spang; print "New URL: ", $url, "\n" if $debug; # Wait for it... return if $debug > 1; $res = $ua->get( $url ); if ( $res->is_success ) { # drop out the countryside colour open( FILE, "| giftopnm | ppmchange '#dad0b4' '#ffffff' | ppmchange '#f0f0e4' '#ffffff' | ppmtogif -quiet -transparent '#ffffff' >$ {filename}" ); binmode( FILE ); syswrite( FILE, $res->content, length( $res->content )); close( FILE ); } else { if ( $debug ) { print STDERR "Failed to get image\n"; print $res->as_string; die; } return undef; } $filename; } 1;