Dynamic PHP Images Tutorial

So I decided to play around with creating a dynamic php image to see how much work was involved and it’s actually pretty easy to do. In my case I wanted to solve the following problem.

I have a trip planned for a weekend coming up and I keep wanting to see what the weather is going to be like for that weekend. It seems everyday the weather.com forecast changes so I keep checking it on a daily basis. My options are

  1. go to the weather.com website, search for the zip code, then select view 10 day forecast or 
  2. create a quick and easy automated way of looking for this information as a graphic. 

Of course, I picked the latter. Here’s what we are about to create:


Here’s the plan. I need to call the weather.com page that lists a 10 day forecast for the zip code I need (ex: http://www.weather.com/weather/tenday/30339?from=36hr_topnav_undeclared). Then I need to search through the HTML of that request and pull out each individual day (there should be 10 in total). Then I need to grab the values for each day such as the date, high and low temperatures, chance of rain, etc. Now I need to construct an image with all that information and save it as a png that I can check anytime I want a 10 day forecast. Every time I request this image, the entire process should take place and the image should return the latest weather forecast data.

Grabbing the HTML request

I am using cURL for this although file_get_contents should work as well. This is what this part of the code looks like:

$zip = isset($_GET['zip']) ?  $_GET['zip'] : 30339;
$ch = curl_init('http://www.weather.com/weather/tenday/'.$zip.'?from=36hr_topnav_undeclared');
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$data = curl_exec($ch);
curl_close($ch);

First I check for the zip code I want then I init curl with the proper params and get the HTML request. If you have questions about the options I’m using here’s the info.

Now I grab the data I need from the HTML using a nasty substr. I was having a “regex just isn’t cooperating with me day” so I quit wasting time and did it the easier way. I haven’t gone back to optimize this code but I probably will later.

// get only 10 day divs
$start_pos = strpos($data, '<div id="tenDay">');
$end_pos = strpos($data, '<!-- tenDayWrap -->');
$data = substr($data, $start_pos, $end_pos - $start_pos);
$items = preg_split('/<!-- day [0-9]+ -->/', $data);
array_shift($items); // get rid of first empty node

Now that I have all the days in an array I can start creating my image object.

$img = @imagecreate(550,140);
$bg = imagecolorallocate($img, 0, 0, 0);
$tc = imagecolorallocate($img, 233, 14, 91);

imagecreate is creating an empty image with the assigned width and height. The next two lines are the background color and the text color that will be used for my image in RGB. Now comes the meat of the process of creating my image:

$x = 5;
$y = 0;
$counter = 0;
foreach($items as $item)
{
preg_match_all('|<[^>]+>(.*)]+>|U', $item, $values);
$day[] = substr($values[1][0], strrpos($values[1][0], '>') + 1);
$date[] = $values[1][1];
$type[] = substr($values[1][3], strrpos($values[1][3], '>') + 1);
$high[] = html_entity_decode(substr($values[1][5], strrpos($values[1][5], '>') + 1));
$low[] = html_entity_decode(substr($values[1][6], strrpos($values[1][6], '>')));
$rain[] = html_entity_decode(substr($values[1][8], strrpos($values[1][8], '>')));
imagestring($img, 1, $x, $y+5, $day[$counter], $tc);
imagestring($img, 1, $x, $y+15, $date[$counter], $tc);
imagestring($img, 1, $x, $y+25, $type[$counter], $tc);
imagestring($img, 1, $x, $y+35, 'High: '.$high[$counter], $tc);
imagestring($img, 1, $x, $y+45, 'Low: '.$low[$counter], $tc);
imagestring($img, 1, $x, $y+55, 'Percip.: '.$rain[$counter], $tc);
$x += 110;
$counter++;
if($counter == 5)
{
$x = 5;
$y = 70;
}
}

I am looping through all my days and setting up variables that I am then creating text objects for on my image. For each item, I create an imagestring passing in the image object, x and y pos, the string and the color. Each item is spaced out 10 pixels from each other in height and 110 px in width. I’m displaying the data as two rows of five items. Now that the image has all the data I need all that is left to do is display it by passing a image/png header and returning the image itself as a png.

header("Content-type: image/png");
imagepng($img);
imagedestroy($img);

And there you go, that’s all needed to get the following result:

This is the 10 day weather forecast for the Atlanta Area. I’m using my .htaccess to call atlanta.png and forward that to my php file with a 30339 zip code. Here’s another example calling a zip code in new york:

As the day progresses, you’ll notice the numbers change since every time these images are requested the php page gets the latest data from weather.com before rendering the new image. As always, questions or comments are welcomed!

4 thoughts on “Dynamic PHP Images Tutorial”

  1. Is that a real-time update? I wonder what if you decide to make it longer like 15 or 20 days what updates/changes are you going to make. By the way I can’t see the image/result even if I reload except for the list of the codes. I agree, scraping pages has more probability of breaking especially when their html are updated so you’ll do it over again. XML feeds and other tools are good to use but still it can break at times.

    1. Hey Kolby, I wrote this a while back and it seems the html markup has changed on the weather channels website so my codes isn’t working. Like mentioned by one of the comments, scraping wasn’t the best approach – I should have used a service instead but it was fun to do at the time. You can modify your code to cache the data for a certain amount of time so you aren’t calling a service every time and rebuilding the image. You do it once, cache your image for x amount of time, and then do it again.

Leave a Reply

Your email address will not be published. Required fields are marked *