<?php
//------------------------------------------------------------------------------
// map-o-net.com image drawing code
//
// version 1.2 - 2007-05-31
//
// by Hourann Bosci <http://hourann.com>
// with thanks to John Prevost and Anders Kaseorg for bug reports,
// Thiadmer Riemersma for the Hilbert-drawing algorithm used here,
// and Randall Munroe of xkcd.com for developing the map!
//
// This code may be redistributed and/or modified under the terms of
// the GNU General Public License, version 2. For details, visit
// http://www.gnu.org/copyleft/gpl.html
//
//------------------------------------------------------------------------------
//
// Randall's map drawing is 672 pixels wide by (672 + big last row = 680) high
// with 16 columns and 16 rows.
//
// Horizontal line y pixel values: 106, 144, 191, 236, 276, 318, 359, 402,
//				   449, 486, 527, 569, 613, 654, 700, 738, 780
//
// The top left corner of the map is at pixel position (34, 104)
//
//------------------------------------------------------------------------------

$heightSlash8 = array( 42, 47, 46, 40, 42, 41, 43, 47, 37, 41, 42, 44, 41, 46, 38, 42 );
$widthSlash8 = 42;
$widthSlash16 = $widthSlash8 / 16;


// The Hilbert curve, represented by a recursive set of directions.
// See hilbert() below, and http://www.compuphase.com/hilbert.htm
$levelDirections = array(
	'LEFT' => array( 'UP', 'RIGHT', 'LEFT', 'DOWN', 'LEFT', 'LEFT', 'DOWN' ),
	'RIGHT' => array( 'DOWN', 'LEFT', 'RIGHT', 'UP', 'RIGHT', 'RIGHT', 'UP' ),
	'UP' => array( 'LEFT', 'DOWN', 'UP', 'RIGHT', 'UP', 'UP', 'RIGHT' ),
	'DOWN' => array( 'RIGHT', 'UP', 'DOWN', 'LEFT', 'DOWN', 'DOWN', 'LEFT' )
);

//------------------------------------------------------------------------------
// Initialisation: read in the address to plot

if ( isset( $_GET['i'] ) && $_GET['i'] != "" ) {

	$ip = $_GET['i'];

	if ( !isset( $_GET['t'] ) || $_GET['t'] == "" || strlen( $_GET['t'] ) > 100 ) {
		// No valid label - just use the IP itself
		$label = $_GET['i'];
	} else {
		// The label provided is valid!
		$label = $_GET['t'];
	}

} else {
	die( "You haven't provided any input!" );
}


//------------------------------------------------------------------------------
// Break the IP into octets and check for validity

$octets = explode( ".", $ip );
if ( count( $octets ) != 4 ) {
	die( "That IP address doesn't seem to be valid! Hit Back and try again." );
}

foreach ( $octets as $number ) {
	if ( !ctype_digit( $number ) || $number > 255 ) {
		die( "That IP address doesn't seem to be valid! Hit Back and try again." );
	}
}


//------------------------------------------------------------------------------
// Step through the Hilbert curve
// Global variables (yeah, I know) will keep track of how far we've travelled
// and our current position

$currentValue = 0;
$rowNum = 0;

$posX = 34;
$posY = 104;

// This script is only concerned with the first two octets -- anything
// smaller would be a change of less than two pixels.
$targetValue = 256 * $octets[0] + $octets[1];

// Recursion starts ... now.
hilbert( 256 );


//------------------------------------------------------------------------------
// JPEG-drawing time!

$posX = round( $posX );
$posY = round( $posY );

$textPosX = $posX + 5;
$textPosY = $posY + 11;

$map = imagecreatefromjpeg( 'map_of_the_internet.jpg' );

// To give "area of certainty" / error bounds
$errorColour = imagecolorallocate( $map, 218, 218, 255 );
imageellipse( $map, $posX, $posY, 20, 20, $errorColour );

// Find out how big our text is before we work with it.
$sizeOfText = imageftbbox ( 9, 0, 'DejaVuSans.ttf', $label );

$rect1 = $textPosX - 1;
$rect2 = $posY;
$rect3 = $textPosX + $sizeOfText[2] + 1;
$rect4 = $posY + 13;

// Switch text position if it's too far to the right.
if ( $textPosX + $sizeOfText[4] > 700 ) {
	$textPosX = $posX - $sizeOfText[4] - 5;
	$rect1 = $textPosX - 1;
	$rect3 = $posX - 4;
}

// Draw a background for the label ...
$white = imagecolorallocate( $map, 255, 255, 255 );
imagefilledrectangle( $map, $rect1, $rect2, $rect3, $rect4, $white );

// ... then the text itself ...
$textColour = imagecolorallocate( $map, 96, 0, 0 );
imagefttext( $map, 9, 0, $textPosX, $textPosY, $textColour, 'DejaVuSans.ttf', $label );

// ... and the location dot.
$dotColour = imagecolorallocate( $map, 204, 0, 0 );
imagefilledellipse( $map, $posX, $posY, 4, 4, $dotColour );

// For "homing beacon" effect.
imageellipse( $map, $posX, $posY, 8, 8, $dotColour );

// Finally, send our work off to the browser.
header("Content-type: image/jpeg");
header('Content-Disposition: inline; filename="map_of_the_internet_' . $ip . '.jpg"');
imagejpeg( $map );

imagedestroy( $map );
exit;


//------------------------------------------------------------------------------


// Step one unit in the given direction.
// This function updates the global tracking variables so there's a
// running tally of our current position, and uses that to determine
// how many pixels to move at each step.
function move( $direction ) {
	global $heightSlash8, $widthSlash16, $rowNum;
	global $currentValue, $targetValue;
	global $posX, $posY;

	if ( $currentValue == $targetValue ) {
		// We're already at our target (which was probably 0)
		return true;
	}

	// Increment our "how many steps?" counter ...
	$currentValue ++;

	// ... and figure out where we're moving.
	if ( $direction == 'UP' ) {
		$rowNum --;
		// Up means subtract previous row height
		$posY = $posY - $heightSlash8[ floor( $rowNum / 16 ) ] / 16;
	} elseif ( $direction == 'DOWN' ) {
		$rowNum ++;
		// Down means add current row height
		$posY = $posY + $heightSlash8[ floor( $rowNum / 16 ) ] / 16;
	} elseif ( $direction == 'LEFT' ) {
		$posX = $posX - $widthSlash16;
	} elseif ( $direction == 'RIGHT' ) {
		$posX = $posX + $widthSlash16;
	}

	// Have we hit the target?
	if ( $currentValue == $targetValue ) {
		return true;
	}

	return false;
}


// Recursively step through the Hilbert curve of the given level
// by starting with the given direction.
// Each cycle has four recursive calls to hilbert()
// the first three of which are followed calls to move()
// ... so for the first six, loop through the array of
// directions with both functions, and make the last call
// to hilbert() separately.
// If $level == 1 we need to omit the calls to hilbert()
// and stop recursing.
function hilbert( $level, $direction='UP' ) {
	global $levelDirections;

	// At the top of this script, the Hilbert curve is
	// defined by sets of directions, each pair of which
	// is used to call hilbert() and move() again.
	$dirs = $levelDirections[ $direction ];

	for ( $i = 0; $i < 6; $i += 2 ) {

		if ( $level != 1 ) {
			$hbResult = hilbert( $level-1, $dirs[ $i ] );
			if ( $hbResult ) {
				return $hbResult;
			}
		}

		$moveResult = move( $dirs[ $i+1 ] );
		if ( $moveResult ) {
			// A non-false return from either function means
			// we've found our target, so we can break out
			// of the recursion.
			return $moveResult;
		}

	}

	if ( $level != 1 ) {
		$hbResult = hilbert( $level-1, $dirs[ 6 ] );
		if ( $hbResult ) {
			return $hbResult;
		}
	}

	return false;
}


?>