Drupal: embed Environment Canada weather feed in node

Drupal

Initially, I used the ecweather module to display weather data. However, I was not happy with the way it worked and the way it displayed the data and it was too much work to get it to my liking. For quite some time I have been using some PHP code in a node to display the text-only weather forecast for Environment Canada using their RSS feed.

If you look at my weather page, you'll see it consists of three parts: the current weather data, watches and warnings, and the five-day forecast.

First you need to obtain the proper RSS feed by selecting the location of your choice on the EC website. Then you need to add that to Drupal's aggregator, for example http://www.weatheroffice.gc.ca/rss/city/ab-53_e.xml.

Then you create a node with the following code. It basically extracts the last rows from the aggregator_item table.

All three parts are basically part of the same large chunk of PHP code on the node but I split it for easier digesting.

The reference in aggregator_item.fid refers to the feed ID. In the admin screen you can see the ID in the link for the RSS feed.

Part 1 is for the current weather conditions:

<?php
// Show Current conditions
$output = '';
$result = db_query("
  SELECT a.*
  FROM {aggregator_item} a
  WHERE (a.fid=2) AND (a.title LIKE '%current conditions%')
  ORDER BY a.timestamp DESC, iid DESC
  LIMIT 1
"
);
while (
$item = db_fetch_object($result)) {
 
$output .= '<div class="weather current"><h3>' . check_plain($item->title) . '</h3>';
  if (
$item->description) {
   
$output .= '<div>'. aggregator_filter_xss($item->description) . '</div>';
  }
 
$output .= '</div>';
}
print
$output;
?>

In part 2 we extract the watches and warnings.

<?php
// Show watches and warnings
$output = '';
$dow = date('l');
$result = db_query("
  SELECT *
  FROM aggregator_item
  WHERE fid=22
    AND (title LIKE '%watch%' OR title LIKE '%warning%')
    AND (title LIKE '%olds%' OR title LIKE '%sundre%')
    AND timestamp = (
      SELECT max(timestamp)
      FROM aggregator_item
      WHERE fid=22
        AND (title LIKE '%watch%' OR title LIKE '%warning%')
        AND (title LIKE '%olds%' OR title LIKE '%sundre%')
    )
  ORDER BY timestamp DESC
"
);
while (
$item = db_fetch_object($result)) {
 
$active = false;
 
$warning_url = '<div>';
 
$warning_url .= 'See <a href="http://www.weatheroffice.gc.ca/warnings/report_e.html?ab1">Weather Warnings for Airdrie, Cochrane, Olds, Sundre</a>.';
 
$warning_url .= '</div>';
 
$pos_ok = strpos(strtolower($item->title), 'no watches or warning');
 
$pos_ended = strpos(strtolower($item->title), 'ended');
 
$pos_warn_end = strpos(strtolower($item->title), 'warning ended');
 
$pos_watch_end = strpos(strtolower($item->title), 'watch ended');
 
$pos_warn = strpos(strtolower($item->title), 'warning');
 
$pos_watch = strpos(strtolower($item->title), 'watch');
 
$header = check_plain($item->title);
  if (
$pos_ok !== false) {
   
$header ='';
   
$class = ' warning-none';
  } elseif (
$pos_ended !== false || $pos_warn_end !== false || $pos_watch_end !== false) {
   
$class = ' warning-end';
  } elseif (
$pos_warn !== false) {
   
$class = ' warning-warn';
   
$active = true;
  } elseif (
$pos_watch !== false) {
   
$class = ' warning-watch';
   
$active = true;
  }
 
$output .= '<div class="weather"><h3 class="pos-' . $pos_ok . ' ' . $class . '">' . $header  . '</h3>';
  if (
$item->description) {
   
$output .= '<div class="' . $class . '">';
   
$output .= aggregator_filter_xss($item->description);
   
$output .= ($active) ? $warning_url : '';
   
$output .= '</div>';
  }
 
$output .= '</div>';
}
print
$output;
?>

Basically, the strpos function is used to test for strings so we can add css classes. I copied the format of Environment Canada and display watches on yellow, warnings on red and on green when they end.

We use a sub-query to retrieve the time stamp of the last update since it is possible that there are multiple watches and/or warnings for any given area.

(Please note the comparison against 'no watches or warning', since the title contained a typo at the time I wrote this.)

Part 3 is to extract the forecast from the table:

<?php
//Show short-term forecast
//select all last rows except
//the current conditions
//and watches/warnings
$output = '';
$dow = date('l');
$result = db_query("
  SELECT *
  FROM `aggregator_item`
  WHERE fid =2
    AND title NOT LIKE '%watch%'
    AND title NOT LIKE '%warning%'
    AND title NOT LIKE '%current condition%'
  ORDER BY iid DESC
  LIMIT 6
"
);
$timestamp = '';
$timestamp_done = false;
while (
$item = db_fetch_object($result)) {
 
$output .= '<div class="weather forecast"><h3>' . check_plain($item->title) . '</h3>';
  if (
$item->description) {
   
$pos = strpos(strtolower($item->description), ' forecast issued');
   
$item_descr = $item->description;
    if (
$pos !== false) {
     
$item_descr = substr_replace($item->description, '', $pos);
      if (!
$timestamp_done) {
       
$timestamp = substr($item->description, $pos + 1);
       
$timestamp_done = true;
      }
    }
   
$output .= '<div>'. aggregator_filter_xss($item_descr) . '</div>';
  }
 
$output .= '</div>';
}
print
$output;
print
'<div class="weather timestamp"><h3></h3>' . $timestamp . '<div>Data provided by Environment Canada</div>' . '</div>';
?>

I tried several other weather sites but they are either using iframes (which I dislike) or the data does not allow for easy integration. One of these days I will get it right but for now, this will do.