Merge Image Layers in PHP

Published: July 17th, 2009 by:

In a previous article, I showed how to Locate the Nearest Radar Station and Display Radar Images. A commenter, mike, pointed out the idea of merging the image layers using PHP instead of layering all the images separately using CSS. Here is how to do it.


Just like the previous article on finding the closest radar station, I am going to use IP geolocation to find the closest radar site. See that previous article for an explanation on how to do that.

Run This Example

<?php

/* replace with your own DB connection code */
require('../includes/database.php');
$db = db_connect();

/* get the IP address and make sure it is an unsigned integer */
$ip = sprintf('%u', ip2long($_SERVER['REMOTE_ADDR']));

/* fetch the location id */
$query = "SELECT locId FROM CityBlocks 
			WHERE $ip BETWEEN startIpNum AND endIpNum LIMIT 1";
$result = mysql_query($query, $db) or die(mysql_error());
$row = mysql_fetch_assoc($result);

/* now fetch the location */
$locId = $row['locId'];
$query = "SELECT * FROM CityLocation WHERE locId = $locId LIMIT 1";
$result = mysql_query($query, $db) or die(mysql_error());
$location = mysql_fetch_assoc($result);

/* offset the coordinates by 3, and find the closest station */
$lat = $location['latitude'] + 3;
$lon = $location['longitude'] - 3;
$query = "SELECT *, SQRT(POW(69.1 * (lat - $lat), 2) + 
			POW(69.1 * ($lon - lon) * COS(lat / 57.3 ), 2 )) AS distance 
			FROM RadarSites ORDER BY distance ASC LIMIT 1";
$result = mysql_query($query, $db) or die(mysql_error());
$radar = mysql_fetch_assoc($result);

/* we are using the snoopy class to download the images */
include('../includes/Snoopy.class.php');
$snoopy = new Snoopy();

/* cache the files locally */
function dl_cache($url, $filename)
{
	if (!file_exists($filename) || filemtime($filename) + 600 < time())
	{
		global $snoopy;
		$snoopy->fetch($url);
		file_put_contents($filename, $snoopy->results);
		$snoopy->results = '';
	}
}

/* download the radar images */
$radar_id = $radar['id'];
dl_cache("http://radar.weather.gov/Overlays/Topo/Short/{$radar_id}_Topo_Short.jpg", "maps/{$radar_id}_Topo_Short.jpg");
dl_cache("http://radar.weather.gov/RadarImg/N0R/{$radar_id}_N0R_0.gif", "maps/{$radar_id}_N0R_0.gif");
dl_cache("http://radar.weather.gov/Overlays/County/Short/{$radar_id}_County_Short.gif", "maps/{$radar_id}_County_Short.gif");
dl_cache("http://radar.weather.gov/Overlays/Highways/Short/{$radar_id}_Highway_Short.gif", "maps/{$radar_id}_Highway_Short.gif");
dl_cache("http://radar.weather.gov/Overlays/Rivers/Short/{$radar_id}_Rivers_Short.gif", "maps/{$radar_id}_Rivers_Short.gif");
dl_cache("http://radar.weather.gov/Overlays/Cities/Short/{$radar_id}_City_Short.gif", "maps/{$radar_id}_City_Short.gif");
dl_cache("http://radar.weather.gov/Legend/N0R/{$radar_id}_N0R_Legend_0.gif", "maps/{$radar_id}_N0R_Legend_0.gif");
dl_cache("http://radar.weather.gov/Warnings/Short/{$radar_id}_Warnings_0.gif", "maps/{$radar_id}_Warnings_0.gif");

/* load the radar layers into an array */
$layers = array();
$layers[] = imagecreatefromjpeg("maps/{$radar_id}_Topo_Short.jpg");
$layers[] = imagecreatefromgif("maps/{$radar_id}_N0R_0.gif");
$layers[] = imagecreatefromgif("maps/{$radar_id}_County_Short.gif");
$layers[] = imagecreatefromgif("maps/{$radar_id}_Highway_Short.gif");
$layers[] = imagecreatefromgif("maps/{$radar_id}_Rivers_Short.gif");
$layers[] = imagecreatefromgif("maps/{$radar_id}_City_Short.gif");
$layers[] = imagecreatefromgif("maps/{$radar_id}_N0R_Legend_0.gif");
$layers[] = imagecreatefromgif("maps/{$radar_id}_Warnings_0.gif");

$image = imagecreatetruecolor(600, 550);


/* merge the layers */
for ($i = 0; $i < count($layers); $i++)
{
	imagecopy($image, $layers[$i], 0, 0, 0, 0, 600, 550);
}

/* we're done! output the image... */
header('Content-type: image/jpeg');
imagejpeg($image);

/**
 * Why do I comment out the PHP closing tag?
 * See: http://phpstarter.net/2009/01/omit-the-php-closing-tag/
 */
/* ?> */

The code from line ~31 on is pretty self-explanatory. Basically, I am caching the maps locally, and then loading the images into PHP image resources. I am putting the images in a PHP array because I want to make it easy add/delete/move layers. For the application I’m using it for, it’s not likely, but your needs may differ.

The important function is imagecopy(), which is defined as:

bool imagecopy ( resource $dst_im , resource $src_im , int $dst_x , int $dst_y , int $src_x , int $src_y , int $src_w , int $src_h )

Since I am copying whole pictures directly on top of each other – all the same size – it’s very easy. You can specify image sizes and location in case you are copying a smaller image onto a larger image, like a logo copyright on a photo, for example.

So there you have it. Although I believe the first method of layering the images via CSS works just fine, this method will work when you need to deliver a single image.


2 Responses to “Merge Image Layers in PHP”

  • Tim Barmann

    Hi Andrew,
    I very much appreciate your great work and your willingness to share it.  Question – why do you use the Snoopy class instead of something like PHP’s file_get_contents() ?
    Thanks again,
    Tim

     

  • Andrew

    The file_get_contents() function does not work in all environments – only the ones that have  allow_url_fopen set to true in the PHP configuration.  I have had more luck across hosting environments that way.

    If you are sure that allow_url_fopen will be enabled where you are using this, then ya, it would probably be easier than including the Snoopy class.

    Glad you find the articles useful! 🙂

     

Leave a Reply





Wordpress doesn't like it when you post PHP code. Go save your code at pastebin, and post the link here.

About the Author

Andrew has been coding PHP applications since 2006, and has plenty of experience with PHP, MySQL, and Apache. He prefers Ubuntu Linux on his desktop and has plenty of experience at managing CentOS web servers. He is the owner of Wells IT Solutions LLC, and develops PHP applications full time for anyone that needs it as well as does desktop computer support locally in the local area. He spends most of his free time exploring new programming concepts and posting on The Webmaster Forums.