From cc94cb66efdf637bcf7ab7c3eab6756156b729ec Mon Sep 17 00:00:00 2001
From: Arvid Rudling <arru@boombox.se>
Date: Wed, 15 Jun 2011 20:43:48 +0200
Subject: [PATCH] Issue 1178150: integration with Node location (location.module), including support for another yr.no API product. Missing features (including upgrade), for review only.

---
 yr_verdata.install |   20 ++-
 yr_verdata.module  |  549 +++++++++++++++++++++++++++++++++++++++++-----------
 2 files changed, 454 insertions(+), 115 deletions(-)

diff --git a/yr_verdata.install b/yr_verdata.install
index 6c2dbf7..d967f88 100644
--- a/yr_verdata.install
+++ b/yr_verdata.install
@@ -88,21 +88,21 @@ function yr_verdata_schema() {
         'description' => 'The name of the location, for sorting purposes.',
         'type' => 'varchar',
         'length' => 100,
-        'not null' => TRUE,
+        'not null' => FALSE,
         'default' => '',
       ),
       'region' => array(
         'description' => 'The name of the region, for sorting purposes.',
         'type' => 'varchar',
         'length' => 100,
-        'not null' => TRUE,
+        'not null' => FALSE,
         'default' => '',
       ),
       'country' => array(
         'description' => 'The name of the country, for sorting purposes.',
         'type' => 'varchar',
         'length' => 100,
-        'not null' => TRUE,
+        'not null' => FALSE,
         'default' => '',
       ),
       'weight' => array(
@@ -119,6 +119,18 @@ function yr_verdata_schema() {
         'not null' => TRUE,
         'default' => 0,
       ),
+       'latitude' => array(
+        'description' => 'Fixed-precision latitude value if location is defined by coordinates.',
+        'type' => 'int',
+        'length' => 6,
+        'not null' => FALSE,
+      ),
+      'longitude' => array(
+        'description' => 'Fixed-precision longitude value if location is defined by coordinates.',
+        'type' => 'int',
+        'length' => 6,
+        'not null' => FALSE,
+      ),
     ),
     'indexes' => array('region' => array('region'), 'country' => array('country')),
     'primary key' => array('yid'),
@@ -126,6 +138,8 @@ function yr_verdata_schema() {
   return $schema;
 }
 
+//TODO: updates to lat/lon table from previous versions
+
 /**
  * Implementation of hook_update_N().
  * This updates from 6.x-1.x to 6.x-2.x.
diff --git a/yr_verdata.module b/yr_verdata.module
index fb26e81..5431707 100644
--- a/yr_verdata.module
+++ b/yr_verdata.module
@@ -240,6 +240,11 @@ function yr_verdata_page_single($yid) {
 }
 
 /**
+ * Number of "fixed" blocks prior to the hard-coded ones, minus 1
+ */
+define("YR_VERDATA_BLOCK_DELTA_START", 2);
+
+/**
  * Implementation of hook_block().
  */
 function yr_verdata_block($op = 'list', $delta = 0, $edit = array()) {
@@ -257,13 +262,17 @@ function yr_verdata_block($op = 'list', $delta = 0, $edit = array()) {
           $load = yr_verdata_load_location($record->yid);
           $location = $load['data'];
           $name = yr_verdata_resolve_name($record);
-          $d = $record->yid + 1; // This is to allow a potential randomblock to have delta 1 while also having multiblocks enabled.
+          $d = $record->yid + YR_VERDATA_BLOCK_DELTA_START; // This is to allow a potential randomblock to have delta 1 while also having multiblocks enabled.
           // Setting the randomblock to "last delta + 1" could give unexpected results if a new location is added afterwards.
           if ($load['status'] == TRUE) $blocks[$d]['info'] = t('Yr weather forecast for @location', array('@location' => $name));
         }
       }
       // If the random block option is enabled, show that one as well.
       if (variable_get('yr_verdata_randomblock', 'off') == 'on') $blocks[1]['info'] = t('Yr random weather forecast');
+      
+      // Node location weather block
+      $blocks[2]['info'] = t('Yr node location weather forecast');
+	  
       // Return them.
       return $blocks;
 
@@ -300,48 +309,77 @@ function yr_verdata_block($op = 'list', $delta = 0, $edit = array()) {
         }
       }
 
-      // If the random block is enabled and placed in a region, we show it. This is not cached.
-      elseif (($delta == 1) && (variable_get('yr_verdata_randomblock', 'off') == 'on')) {
-        $load = yr_verdata_load_location(-1);
-        $location = $load['data'];
-        $name = yr_verdata_resolve_name($location); // Unclean, but cleaned by l().
-        $block['subject'] = l(t('Forecast for !location', array('!location' => $name)), 'forecast/' . $location->yid);
-        $block['content'] = yr_verdata_generate($location, 'block') . yr_verdata_credit_link($location->url, TRUE);
-      }
+    // If the random block is enabled and placed in a region, we show it. This is not cached.
+  	elseif (($delta == 1) && (variable_get('yr_verdata_randomblock', 'off') == 'on')) {
+  		$load = yr_verdata_load_location();
+  		$location = $load['data'];
+  		$name = yr_verdata_resolve_name($location); // Unclean, but cleaned by l().
+  		$block['subject'] = l(t('Forecast for !location', array('!location' => $name)), 'forecast/' . $location->yid);
+  		$block['content'] = yr_verdata_generate($location, 'block') . yr_verdata_credit_link($location->url, TRUE);
+  	}
+  	  
+  	//Node location weather block.
+    elseif ($delta == 2) {
+      if (user_access('access content') and
+      arg(0) == 'node' and is_numeric(arg(1))) {
+      $node = node_load(arg(1));
+        
+        if ( isset($node->locations)) {
+          foreach ($node->locations as $location) {
+            if (($location['latitude'] != 0) or ($location['longitude'] != 0)) {
+            $lat= $location['latitude'];
+            $lon= $location['longitude'];
+            }
+          }
+          
+          $load = yr_verdata_load_location($lat,$lon);
+          $location = $load['data'];
+          if ($load['status'] == TRUE) {
+            $block['subject'] = t('Local forecast');
+            $block['content'] = yr_verdata_generate($location, 'block') . yr_verdata_credit_link($location->url, TRUE);
+            
+            //Set the cache for the block we just generated.
+            $maxage = variable_get('yr_verdata_maxage', 21600);
+            cache_set($cache_id, $output, 'cache', time() + $maxage);
+          }
+          
+        } //if node has location
+      } //if viewing node and user has permission
+    }
 
-      // Create a block for a given location.
-      else {
-        // Check for the multiblock setting here as well as in hook_block_info(),
-        // because otherwise if 'yr_multiblocks' is swithced off while multiblocks
-        // are assigned to regions, they won't show up on the administrative 'blocks'
-        // page, but still be visible in whatever region they were assigned to.
-        // The same goes for the randomblock above.
-        if (variable_get('yr_verdata_multiblocks', 'off') == 'on') {
-          // Get the yid from the delta.
-          $l = $delta - 1;
-          // Check the cache.
-          $cache_id = 'yr_verdata_block_' . $l;
-          if ($cache = cache_get($cache_id, 'cache') && $cache->expire > time()) {
-            $block['content'] = $cache->data;
+    // Create a block for a manually entered location.
+    else {
+      // Check for the multiblock setting here as well as in hook_block_info(),
+      // because otherwise if 'yr_multiblocks' is swithced off while multiblocks
+      // are assigned to regions, they won't show up on the administrative 'blocks'
+      // page, but still be visible in whatever region they were assigned to.
+      // The same goes for the randomblock above.
+      if (variable_get('yr_verdata_multiblocks', 'off') == 'on') {
+        // Get the yid from the delta.
+        $l = $delta - YR_VERDATA_BLOCK_DELTA_START;
+        // Check the cache.
+        $cache_id = 'yr_verdata_block_' . $l;
+        if ($cache = cache_get($cache_id, 'cache') && $cache->expire > time()) {
+          $block['content'] = $cache->data;
+        }
+        else {
+          // Load the location.
+          $load = yr_verdata_load_location($l);
+          $location = $load['data'];
+          if ($load['status'] == TRUE) {
+            $name = yr_verdata_resolve_name($location); // Unclean, but cleaned by l().
+            $block['subject'] = l(t('Forecast for !location', array('!location' => $name)), 'forecast/' . $location->yid);
+            $block['content'] = yr_verdata_generate($location, 'block') . yr_verdata_credit_link($location->url, TRUE);
+            // Set the cache for the block we just generated.
+            $maxage = variable_get('yr_verdata_maxage', 21600);
+            cache_set($cache_id, $output, 'cache', time() + $maxage);
           }
           else {
-            // Load the location.
-            $load = yr_verdata_load_location($l);
-            $location = $load['data'];
-            if ($load['status'] == TRUE) {
-              $name = yr_verdata_resolve_name($location); // Unclean, but cleaned by l().
-              $block['subject'] = l(t('Forecast for !location', array('!location' => $name)), 'forecast/' . $location->yid);
-              $block['content'] = yr_verdata_generate($location, 'block') . yr_verdata_credit_link($location->url, TRUE);
-              // Set the cache for the block we just generated.
-              $maxage = variable_get('yr_verdata_maxage', 21600);
-              cache_set($cache_id, $output, 'cache', time() + $maxage);
-            }
-            else {
-              return array();
-            }
+            return array();
           }
         }
       }
+    }
       return $block;
   }
 }
@@ -422,37 +460,50 @@ function yr_verdata_generate(&$location, $context, $name = '') {
   // Load the xml for use later on.
   $data = simplexml_load_file(_yr_verdata_local_file($location));
   $location->xml = $data;
+  $location->variant = _yr_verdata_xml_variant ($data);
+    
+  switch ($location->variant) {
+    case 'varsel.xml' :
+      $table = $data -> forecast -> tabular;
+      break;
+    case 'api location 1.8' :
+      $table = $data -> product;
+      break;
+    default :
+      watchdog('yr_verdata', "Yr verdata couldn't determine type of returned XML data (@val)", array('@val' => $location -> variant), WATCHDOG_ERROR);
+  }
+  
   // Set up an array to send to the theme function.
   $variables = array();
   switch ($context) { // Based on what context, we pick parts of the xml and prepare it for display.
     case 'table' :
-      $variables['symbol'] = theme('yr_verdata_symbol', array('symbol' => $data->forecast->tabular->time[0]->symbol, 'period' => $data->forecast->tabular->time[0]['period'])); // Clean.
-      $variables['wind'] = theme('yr_verdata_wind', array('dir' => $data->forecast->tabular->time[0]->windDirection, 'speed' => $data->forecast->tabular->time[0]->windSpeed)); // Clean.
-      $variables['temp'] = theme('yr_verdata_temp', $data->forecast->tabular->time[0]->temperature); // Clean.
-      $variables['time'] = date(_yr_verdata_date_format(), strtotime($data->forecast->tabular->time[0]['from'])); // Clean.
+      _yr_verdata_parse_entry($table->time, $location->variant, 0, $variables);
       break;
 
     case 'information' : // For the page, we first want a part with basic information and upcoming weather.
-      if ($location->lang == 'en') {
-        $trans_loctype = _yr_verdata_translatable();
-        // If the locationtype is not in the translatable array, we just call it "Place".
-        $loctype = (in_array((string) $data->location->type, $trans_loctype)) ? $trans_loctype[(string) $data->location->type] : t('Place');
-      }
-      else {
-        $loctype = check_plain($data->location->type);
-      }
-      $variables['location'] = t('!type in @country, @alt meters above sealevel', array('!type' => $loctype, '@country' => $data->location->country, '@alt' => $data->location->location['altitude'])); // Clean. $loctype is implicitly clean because it is a string from a translation.
+      if($location -> variant == 'varsel.xml') {
+        if($location -> lang == 'en') {
+          $trans_loctype = _yr_verdata_translatable();
+          // If the locationtype is not in the translatable array, we just call it "Place".
+          $loctype = (in_array((string)$data -> location -> type, $trans_loctype)) ? $trans_loctype[(string)$data -> location -> type] : t('Place');
+        } else {
+          $loctype = check_plain($data -> location -> type);
+        }
+        $variables['location'] = t('!type in @country, @alt meters above sealevel', array('!type' => $loctype, '@country' => $data -> location -> country, '@alt' => $data -> location -> location['altitude']));
+        // Clean. $loctype is implicitly clean because it is a string from a translation.
 
-      $variables['lastupdate'] = t('Last updated @lastup', array('@lastup' => date(_yr_verdata_date_format(), strtotime($data->meta->lastupdate)))); // Clean.
-      if (isset($data->sun['never_rise'])) {
-        $variables['sun']['never_rise'] = t("Polar night, the sun doesn't rise.");
-      }
-      elseif (isset($data->sun['never_set'])) {
-        $variables['sun']['never_set'] = t("Midnight sun, the sun doesn’t set.");
-      }
-      else {
-        $variables['sun']['rise'] = t('Sunrise:') . ' ' . date(_yr_verdata_date_format(), strtotime($data->sun['rise'])); // Clean.
-        $variables['sun']['set'] = t('Sunset:') . ' ' . date(_yr_verdata_date_format(), strtotime($data->sun['set'])); // Clean.
+        $variables['lastupdate'] = t('Last updated @lastup', array('@lastup' =>  date(_yr_verdata_date_format(),  strtotime($data -> meta -> lastupdate))));
+        // Clean.
+        if(isset($data -> sun['never_rise'])) {
+          $variables['sun']['never_rise'] = t("Polar night, the sun doesn't rise.");
+        } elseif(isset($data -> sun['never_set'])) {
+          $variables['sun']['never_set'] = t("Midnight sun, the sun doesn’t set.");
+        } else {
+          $variables['sun']['rise'] = t('Sunrise:') . ' ' . date(_yr_verdata_date_format(),  strtotime($data -> sun['rise']));
+          // Clean.
+          $variables['sun']['set'] = t('Sunset:') . ' ' . date(_yr_verdata_date_format(),  strtotime($data -> sun['set']));
+          // Clean.
+        }
       }
       // Link to yr.no.
       $variables['links']['yr'] = l(t('Forecast for !location at yr.no', array('!location' => $location->name)), $location->url); // Cleaned by l().
@@ -460,21 +511,21 @@ function yr_verdata_generate(&$location, $context, $name = '') {
       // Link to google maps. TODO Make other map services available as a choice?
       $gmaps_url = 'http://maps.google.com/maps?ie=UTF8&hl=en&z=12&ll=' . $data->location->location['latitude'] . ',' . $data->location->location['longitude'];
       $variables['links']['gmaps'] = l(t('View !location at Google Maps', array('!location' => $location->name)), $gmaps_url); // Cleaned by l().
-      $variables['upcoming-forecast'] = yr_verdata_generate_forecastboxes($data);
+      $variables['upcoming-forecast'] = yr_verdata_generate_forecastboxes($location);
       $variables['timezone'] = $data->location->timezone['id']; // Cleaned by t() in the theme function.
       break;
 
     case 'forecast' :
       // And add the four next forecast periods that are available.
-      $variables = yr_verdata_generate_forecastboxes($data, 4, 22);
+      $variables = yr_verdata_generate_forecastboxes($location, 4, 22);
       break;
 
     case 'radar' :
       $variables = yr_verdata_radar($data);
       break;
 
-    case 'block' :
-      $variables = yr_verdata_generate_forecastboxes($data, 0, 1, $name); // Just the first period for the block.
+    case 'block' :      
+      $variables = yr_verdata_generate_forecastboxes($location, 0, variable_get('yr_verdata_block_num_forecasts',6), $name);
       $variables['yid'] = $location->yid;
       break;
   }
@@ -482,6 +533,11 @@ function yr_verdata_generate(&$location, $context, $name = '') {
 }
 
 /**
+ * Duration of forecast period in hours
+ */
+define('YR_VERDATA_PERIOD_LENGTH',6);
+
+/**
  * Function for generating one or more forecast boxes for given periods.
  *
  * @param $data
@@ -497,36 +553,65 @@ function yr_verdata_generate(&$location, $context, $name = '') {
  * @return
  * Returns an array of themed forecast boxes.
  */
-function yr_verdata_generate_forecastboxes($data, $start = 0, $num = 4, $name = '') {
-  $key = 0;
-  $boxes = array();
-  $periods = $data->forecast->tabular->time;
-  $last = 0;
-  foreach ($periods as $forecast) {
-    if (($num - $key) == 1) $boxes[$key]['last'] = TRUE;
-    if ($key == $num) break; // Stop the loop once we hit the desired number of boxes.
-    if ($start > $key) { // This enables the ability to skip the first $start periods.
-      $key++;
-      continue;
+function yr_verdata_generate_forecastboxes($location, $start =0, $num =4, $name ='') {
+
+    //determine XML root node depending on format variant
+    switch ($location->variant) {
+      case ('varsel.xml') :
+        $entries = $location -> xml -> forecast -> tabular -> time;
+        break;
+      case ('api location 1.8') :
+        $entries = $location -> xml -> product -> time;
+        break;
     }
-    $boxes[$key]['time'] = date(_yr_verdata_date_format(), strtotime($forecast['from'])); // Clean.
-    $boxes[$key]['symbol'] = theme('yr_verdata_symbol', array('symbol' => $forecast->symbol, 'period' => $forecast['period'])); // Clean.
-    $boxes[$key]['wind'] = theme('yr_verdata_wind', array('dir' => $forecast->windDirection, 'speed' => $forecast->windSpeed)); // Clean.
-    $boxes[$key]['temp'] = theme('yr_verdata_temp', $forecast->temperature); // Clean.
-    $boxes[$key]['precip'] = theme('yr_verdata_precip', $forecast->precipitation); // Clean.
-    $boxes[$key]['pressure'] = theme('yr_verdata_pressure', array('pressure' => $forecast->pressure)); // Clean.
-    $boxes[$key]['period'] = check_plain($forecast['period']); // This is used for adding markup in the long-term forecast.
-    if ($boxes[$key]['period'] == 0) $boxes[$key]['day'] = format_date(strtotime($forecast['to']), 'custom', 'l', NULL, NULL); // Add the dayname. We use $forecast['to'] to avoid any wrong daynames because of potential timezone issues.
-    if (($last == 3) && ($boxes[$key]['period'] == 2)) { // For the last days, which are just one box per day.
-      $boxes[$key]['day'] = t('Following days');
-      $boxes[$key]['following'] = TRUE;
+
+    $key = 0;
+    $boxes = array();
+    $last_time = 0;
+    $index = 0;
+
+    while($key < $num) {
+      if($start > $key) { // This enables the ability to skip the first $start periods.
+        $key++;
+        continue ;
+      }
+      $next_box = array();
+      $index = _yr_verdata_parse_entry($entries, $location -> variant, $index, $next_box);
+
+      //a number of check to determine if this item should be included in output:
+
+      //only show one forecast in a period (API 1.8 supplies several)
+      if(array_key_exists('time_main', $next_box) && ($next_box['time_main'] - $last_time) < 60 * 60 * YR_VERDATA_PERIOD_LENGTH) {
+        continue ;
+      }
+
+      if($next_box['time_main'] > (time() + 43200)) {
+        //more than 12 hours ahead
+
+        $next_box['day'] = format_date($next_box['time_to'], 'custom', 'l', NULL, NULL);
+        //$next_box['day'] = t('Following days');
+        //$next_box['following'] = TRUE;
+
+        //only show period 2 in the following days, skip loop otherwise
+        if(array_key_exists('period', $next_box) && $next_box['period'] != 2) {
+          continue ;
+        }
+      }
+
+      //mark as last item if applicable
+      if(($num - $key) == 1)
+        $next_box['last'] = TRUE;
+
+      $last_time = $next_box['time_main'];
+      $boxes[$key] = $next_box;
+
+      $key++;
     }
-    $last = $boxes[$key]['period'];
-    $key++;
+
+    if(!empty($name))
+      $boxes['name'] = check_plain($name);
+    return $boxes;
   }
-  if (!empty($name)) $boxes['name'] = check_plain($name);
-  return $boxes;
-}
 
 /**
  * Function for generating the necessary stuff for a radarimage.
@@ -538,9 +623,18 @@ function yr_verdata_generate_forecastboxes($data, $start = 0, $num = 4, $name =
  * the link to the radarpage at the location's page at yr.no and 'text'
  * for the text used as alt and title for the image.
  */
-function yr_verdata_radar($data) {
-    $lat = (int) $data->location->location['latitude'];
-    $long = (int) $data->location->location['longitude'];
+function yr_verdata_radar($location) {
+    $data = $location -> xml;
+    switch ($location->variant) {
+      case ('varsel.xml') :
+        $lat = (int)$data -> location -> location['latitude'];
+        $long = (int)$data -> location -> location['longitude'];
+        break;
+      case ('api location 1.8') :
+        $lat = (int)$data -> product -> time[0] -> location['latitude'];
+        $lon = (int)$data -> product -> time[0] -> location['longitude'];
+        break;
+    }
     $radarsite = FALSE;
     // Figure out which radarimage to use. Start narrow in the south and expand if lat/long is outside range.
     // Finally set $showradar = FALSE; if no compatible lat/long range is found.
@@ -595,22 +689,78 @@ function yr_verdata_radar($data) {
  * Returns an array with the basic location information in an object and a status.
  * If no location was found, the status is FALSE, and the data returned is a message.
  */
-function yr_verdata_load_location($yid) {
-  if ($yid == -1) {
-    $result = db_query("SELECT * FROM {yr_verdata} ORDER BY rand() LIMIT 1"); // This might be slow on large tables, but we'll probably never see anyone going over two digits on their drupal site. If so, they can post an issue, or simply not use the random block.
-  }
-  else {
-    $result = db_query("SELECT * FROM {yr_verdata} WHERE yid = %d", $yid);
+function yr_verdata_load_location() {
+  $mode = func_num_args();
+  switch ($mode) {
+
+    //check if location exists in DB
+    case 0 :
+
+    //random location
+      $result = db_query("SELECT * FROM {yr_verdata} ORDER BY rand() LIMIT 1");
+      // This might be slow on large tables, but we'll probably never see anyone going over two digits on their drupal site. If so, they can post an issue, or simply not use the random block.
+      break;
+    case 1 :
+
+    //named location
+      $yid = func_get_arg(0);
+      $result = db_query("SELECT * FROM {yr_verdata} WHERE yid = %d", $yid);
+      break;
+    case 2 :
+
+    //lat/lon location
+      $db_lat = _yr_verdata_figure_to_fixed(func_get_arg(0));
+      $db_lon = _yr_verdata_figure_to_fixed(func_get_arg(1));
+
+      $result = db_query("SELECT * FROM {yr_verdata} WHERE latitude = %d AND longitude = %d", $db_lat, $db_lon);
+      break;
   }
+	
   if ($record = db_fetch_object($result)) {
     $record->filepath = _yr_verdata_local_file($record);
     return array('data' => $record, 'status' => TRUE);
   }
   else {
-    $msg = t('No location found with the given ID.');
-    if (user_access('administer yr_verdata')) $msg .= ' ' . _yr_verdata_addmore_msg();
-    return array('data' => $msg, 'status' => FALSE);
-  }
+    if($mode == 2) {
+    //lat/lon locations are generated on the fly if not already present in db
+
+    $url = _yr_verdata_get_latlon_url($db_lat, $db_lon);
+
+    //$lang = 'nb';
+
+    $file = md5($url) . '.xml';
+    $weight = 0;
+
+    //FIXME: some duplication of code, merge with yr_verdata_add_form_submit?
+    //FIXME: don't know any way to link to a lat/lon location as yr.no webpage - inserted dummy url
+    $query = "INSERT INTO {yr_verdata} (url, lang, file, weight, name, region, country, latitude, longitude) VALUES ('%s', '%s', '%s', %d, NULL, NULL, NULL, %d, %d)";
+    if(db_query($query, 'http://www.yr.no/', $lang, $file, $weight, $db_lat, $db_lon)) {
+        //drupal_set_message(t('Location added.'));
+        $id = db_last_insert_id('yr_verdata', 'yid');
+        $record = new stdClass;
+        $record -> file = $file;
+        //$record->url = $url;
+        $record -> yid = $id;
+        $record -> latitude = $db_lat;
+        $record -> longitude = $db_lon;
+
+        _yr_verdata_refresh_xml($record);
+        // all-new location; no cache flush necessary (?)
+        //cache_clear_all('yr_verdata', 'cache_page', TRUE);
+        return array('data' => $record, 'status' => TRUE);
+      } else {
+        $error_msg = t('Could not initiate forecasts for @lat,@lon. If this problem persists, the administrator should be notified.', array('@lat' =>   _yr_verdata_fixed_to_figure($db_lat), '@lon' =>   _yr_verdata_fixed_to_figure($db_lon)));
+      }
+    }
+
+    else {
+      $error_msg = t('No location found with the given ID.');
+    }
+
+    //if we haven't returned yet, location load failed:
+    if (user_access('administer yr_verdata')) $error_msg .= ' ' . _yr_verdata_addmore_msg();
+      return array('data' => $error_msg, 'status' => FALSE);
+    }
 }
 
 /**
@@ -955,16 +1105,18 @@ function theme_yr_verdata_block($vars) {
   $output = '';
   if (isset($vars['name'])) $output .= '<h4>' . l($vars['name'], 'forecast/' . $vars['yid']) . '</h4>';
   $output .= '<div class="yr-forecast-block-box">';
-    $output .= '<p class="yr-period-time">' . $vars[0]['time'] . '</p>';
+  for ($entry_num=0;isset($vars[$entry_num]);$entry_num++) {
+    $output .= '<p class="yr-period-time">' . $vars[$entry_num]['time'] . '</p>';
     $output .= '<div class="yr-period-forecast">';
       $output .= '<p class="yr-symbols">';
-        $output .= '<span class="yr-symbol">' . $vars[0]['symbol'] . '</span>';
-        $output .= '<span class="yr-wind">' . $vars[0]['wind'] . '</span>';
-        $output .= $vars[0]['temp'];
+        $output .= '<span class="yr-symbol">' . $vars[$entry_num]['symbol'] . '</span>';
+        $output .= '<span class="yr-wind">' . $vars[$entry_num]['wind'] . '</span>';
+        $output .= $vars[$entry_num]['temp'];
       $output .= '</p>';
-      $output .= '<p class="yr-precip">' . $vars[0]['precip'] . '</p>';
-      $output .= '<p class="yr-pressure">' . $vars[0]['pressure'] . '</p>';
+      $output .= '<p class="yr-precip">' . $vars[$entry_num]['precip'] . '</p>';
+      $output .= '<p class="yr-pressure">' . $vars[$entry_num]['pressure'] . '</p>';
     $output .= '</div>';
+    }
   $output .= '</div>';
   return $output;
 }
@@ -974,6 +1126,169 @@ function theme_yr_verdata_block($vars) {
  **********************/
 
 /**
+   * Determine which API variant the supplied XML data was generated by
+   *
+   * @param $data
+   * Parsed XML forecast data
+   *
+   * @return
+   * Constants representing the supported variants or -1 for unknown
+   *
+   */
+
+  function _yr_verdata_xml_variant($data) {
+
+    if(isset($data -> location -> name)) {
+      return 'varsel.xml';
+    }
+
+    if(isset($data -> product)) {
+      return 'api location 1.8';
+    }
+
+    //unknown
+    return -1;
+  }
+
+  /**
+   * Convert floating-point geo coordinates into fixed-precision ints for storing in database.
+   *
+   * @param $val
+   * Floating-point geo coordinate
+   * @return
+   * Returns an integer 10000x the actual figure and rounded to a degree which may change in the future
+   *
+   */
+
+  function _yr_verdata_figure_to_fixed($val) {
+    return round($val * 10) * 1000;
+  }
+
+  /**
+   * Convert fixed-precision int coordinates into floating-point numbers.
+   *
+   * @param $val
+   *
+   * @return
+   * Floating-point coordinate corresponding to the input fixed-point value
+   *
+   */
+
+  function _yr_verdata_fixed_to_figure($val) {
+    return $val * 0.0001;
+  }
+
+  /**
+   * Produce an Yr.no feed URL for suitably rounded lat/lon that is stable enough
+   * to be used in generating the filename hash
+   *
+   * @param lat
+   * Fixed-point (db format) latitude of location
+   *
+   * @param lon
+   * Fixed-point (db format) longitude of location
+   *
+   * @return
+   * an Yr.no feed URL for the specified location
+   */
+
+  function _yr_verdata_get_latlon_url($lat, $lon) {
+    return  sprintf("http://api.met.no/weatherapi/locationforecast/1.8/?lat=%.1f;lon=%.1f", _yr_verdata_fixed_to_figure($lat), _yr_verdata_fixed_to_figure($lon));
+  }
+
+  /**
+   * Retrieve the core forecast data (excl. time) from XML tree
+   */
+  function _yr_verdata_get_forecast_item($time_entry, $variant) {
+    switch ($variant) {
+
+      case ('varsel.xml') :
+        return $time_entry;
+        break;
+      case ('api location 1.8') :
+        return $time_entry -> location;
+        break;
+    }
+  }
+
+  /**
+   *  Retrieve time variables of forecast entry from XML tree
+   */
+  function _yr_verdata_parse_portion_time($entry, $variant, &$output) {
+    $output['time_to'] = strtotime($entry['to']);
+
+    switch ($variant) {
+      case ('varsel.xml') :
+        $output['time_from'] = strtotime($entry['from']);
+        $period = check_plain($entry['period']);
+
+        break;
+      case ('api location 1.8') :
+        $output['time_from'] = strtotime($entry['from']);
+        $_dat = getdate($output['time_to']);
+
+        //no period value is supplied for API 1.8, we'll have to calculate our own
+        $period = floor($_dat['hours'] / $url = YR_VERDATA_PERIOD_LENGTH);
+
+        break;
+    }
+
+    return $period;
+  }
+
+  /**
+   * Extract one entry from XML weather data
+   *
+   * @param $xml_root
+   * Array of time entries in XML data
+   *
+   * @param $index
+   * Index in data where the requested entry starts (which will NOT correspond to
+   * forecast # for API 1.8, except for index 0)
+   *
+   * @param $output
+   * Array where the extracted data goes
+   *
+   * @return
+   * Index where following item starts
+   */
+  function _yr_verdata_parse_entry($xml_root, $variant, $index, &$output) {
+    do {
+      $data = _yr_verdata_get_forecast_item($xml_root[$index], $variant);
+
+      $time_period = _yr_verdata_parse_portion_time($xml_root[$index], $variant, $output);
+
+      if(isset($data -> temperature)) {
+        $output['time_main'] = $output['time_to'];
+        $output['period'] = $time_period;
+
+        $output['time'] = date(_yr_verdata_date_format(), $output['time_from']);
+        // Clean.
+        $output['wind'] = theme('yr_verdata_wind', array('dir' => $data -> windDirection, 'speed' => $data -> windSpeed));
+        // Clean.
+        $output['temp'] = theme('yr_verdata_temp', $data -> temperature);
+        // Clean.
+        $output['pressure'] = theme('yr_verdata_pressure', array('pressure' => $data -> pressure));
+        // Clean.
+      }
+
+      if(isset($data -> precipitation)) {
+        $output['time_precip'] = $output['time_to'];
+
+        $output['symbol'] = theme('yr_verdata_symbol', array('symbol' => $data -> symbol, 'period' => $output['period']));
+        // Clean.
+        $output['precip'] = theme('yr_verdata_precip', $data -> precipitation);
+        // Clean.
+      }
+
+      $index++;
+    } while (! array_key_exists ('time_main',$output) || !array_key_exists ('time_precip',$output) ||
+    ($output['time_main'] != $output['time_precip']));
+
+    return $index;
+  }
+  
+/**
  * Message functions for centralizing messages used multiple places.
  */
 function _yr_verdata_addmore_msg() {
@@ -1090,7 +1405,16 @@ function _yr_verdata_refresh_xml($location) {
  * A location object from the {yr_verdata} table.
  */
 function _yr_verdata_fetch_xml($location) {
-  $feed = check_url('http://www.yr.no/' . drupal_urlencode(drupal_substr(rawurldecode($location->url), 17)) . 'varsel.xml');
+	$is_coord_feed = (($location->latitude != 0) or ($location->longitude != 0))? 1 : 0;
+	
+  if($is_coord_feed) {
+    //lat/lon location
+    $feed = check_url(_yr_verdata_get_latlon_url($location -> latitude, $location -> longitude));
+  } else {
+    //manually entered, named location
+    $feed = check_url('http://www.yr.no/' . drupal_urlencode(drupal_substr(rawurldecode($location -> url), 17)) . 'varsel.xml');
+  }
+	
   // Initialize new cURL session.
   $ch = curl_init();
   // Test the connection and see if the returned data is actually a valid forecast.
@@ -1102,7 +1426,7 @@ function _yr_verdata_fetch_xml($location) {
   $data = curl_exec($ch);
   $info = curl_getinfo($ch);
   $valid_xml = simplexml_load_string($data);
-  if ($info['http_code'] == '200' && (isset($valid_xml->location->name))) {
+  if ($info['http_code'] == '200' && ($is_coord_feed || isset($valid_xml->location->name))) {
     // The forecast seems ok, we can return it.
     return $data;
   }
@@ -1148,6 +1472,7 @@ function _yr_verdata_temperature($temp) {
   $temperature = array();
   switch ($temp['unit']) {
     case 'celsius' :
+    case 'celcius' : //catch misspelling in 1.8 API as of 2011-06-09
       $temperature['celsius'] = (int)$temp['value'];
       $temperature['fahrenheit'] = round(($temp['value'] * 9 / 5) + 32);
       $temperature['kelvin'] = $temp['value'] + 273;
-- 
1.7.3.3

