Merge Image Layers in PHP
Published: July 17th, 2009 by: Andrew
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.
<?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.
Tim Barmann
Mar 3rd, 2010
12:51 pm
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
Mar 3rd, 2010
1:07 pm
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! 🙂