# Read a file with cities into a graph
# Uses shadow classes

use graph;
package graph;

%Cities;              # Hash table mapping cities to nodes
%Nodes;               # Mapping of Node indicies to cities
%Locations;           # Mapping of city names to locations

sub read_cities {
    my $filename = shift;
    open(CITIES,$filename);
    while (<CITIES>) {
	chop;
	my @a = split(/, +/);
	my $node1;
	my $node2;
	# Check to see if a given city is already a node
	if (!exists $Cities{$a[0]}) {
	    $node1 = new Node;
	    $Cities{$a[0]} = $node1;
	    $Nodes{$node1->{v}} = $a[0];
	} else {
	    $node1 = $Cities{$a[0]};
	}
	if (!exists $Cities{$a[1]}) {
	    $node2 = new Node;
	    $Cities{$a[1]} = $node2;
	    $Nodes{$node2->{v}} = $a[1];
	} else {
	    $node2 = $Cities{$a[1]};
	}
	# Add edges
	$node1->addedge($node2,$a[2]);
	$node2->addedge($node1,$a[2]);
    }
    close CITIES;
}


sub read_locations {
    my $filename = shift;
    open(LOCATIONS,$filename);
    while (<LOCATIONS>) {
	chop;
	my @a = split(/, +/);
	my $loc;
	# Check to see if this is a city I know about
	if (exists $Cities{$a[0]}) {
	    # Create a new location
	    $loc = new Location($a[0]);
	    my @coords = split(' ',$a[1]);
	    %$loc = (lat_degrees => $coords[0],
		     lat_minutes => $coords[1],
		     lat_seconds => $coords[2],
		     lat_direction => $coords[3],
		     long_degrees => $coords[4],
		     long_minutes => $coords[5],
		     long_seconds => $coords[6],
		     long_direction => $coords[7]);
	    my $v = $Cities{$a[0]}->{v};
	    $Locations{$v} = $loc;
	}
    }
    close LOCATIONS;
}

%visit;

sub breadth_search {
    my $node1 = shift;
    my $node2 = shift;
    my @queue;
    %visit = ();

    my $dest = $node2->{v};
    # Put starting node into queue

    push @queue, $node1;
    $visit{$node1->{v}} = $node1->{v};

    while (@queue) {
	my $n = shift @queue;
	if ($n->{v} == $node2->{v}) {
	    return 1;
	}

	# Put children onto the queue
	my $e = $n->{adj};
	while (defined($e)) {
	    if (!exists $visit{$e->{node}->{v}}) {
		push @queue, $e->{node};
		$visit{$e->{node}->{v}} = $n->{v};
	    }
	    $e = $e->{next};
	}
    }
    return 0;
}


sub find_route {
    my $start = shift;
    my $dest = shift;
    my $im = shift;
    
    # Lookup nodes from names
    if ((!exists $Cities{$start}) || (!exists $Cities{$dest})) {
	return;
    }

    my $node1 = $Cities{$start};
    my $node2 = $Cities{$dest};
    my $found = breadth_search($node1,$node2);
    my @path;
    if ($found) {
	my $v = $node2->{v};
	delete $visit{$node1->{v}};
	while (exists($visit{$v})) {
	    unshift @path,$Nodes{$v};
	    $v = $visit{$v};
	}
	unshift @path,$start;
	my $last = $node1;
	foreach $c (@path) {
	    $node2 = $Cities{$c};
	    if ((exists $Locations{$last->{v}}) && (exists $Locations{$node2->{v}})) {
		my $loc1 = $Locations{$last->{v}};
		my $loc2 = $Locations{$node2->{v}};
		plot_cities($im,$loc1,$loc2,$red);
	    }
	    $last = $node2;
	}
    } else {
	print "    You can't get there from here\n";
    }
}

read_cities("cities");
read_locations("locations");

$im = gdImageCreate(500,500);
$white = gdImageColorAllocate($im,255,255,255);
$black = gdImageColorAllocate($im,0,0,0);
$red = gdImageColorAllocate($im,255,0,0);

$xmin = -130;
$xmax = -90;
$ymin = 30;
$ymax = 50;

# Make a plot of the entire graph


@loc = each %Cities;
while (@loc) {
    my $city = $loc[0];
    my $node = $Cities{$city};
    if (exists $Locations{$node->{v}}) {
	my $loc1 = $Locations{$node->{v}};
	plot_cities($im,$loc1,$loc1,$black);
    }
    @loc = each %Cities;
}

find_route("Denver","Seattle",$im);
dump_gif($im,"test.gif");

print "Output written to 'test.gif'\n";


syntax highlighted by Code2HTML, v. 0.9.1