--- exif.module	2007-05-29 00:21:06.000000000 +1000
+++ exif.module.new	2008-10-21 13:47:40.000000000 +1100
@@ -1,5 +1,6 @@
 <?php
 // $Id: exif.module,v 1.4.2.2 2007/05/28 14:21:06 davidlesieur Exp $
+// vim:set ft=php:
 
 include_once drupal_get_path('module', 'exif') .'/pel/PelJpeg.php';
     
@@ -22,6 +23,76 @@ function exif_menu($may_cache) {
   return $items;
 }
 
+function _exif_node_file($node) {
+  /* Return the original file, or FALSE if not there or JPEG */
+  $file = file_create_path($node->images['_original']);
+  if (!file_exists($file)) {
+    watchdog('exif', t('Image %file not found.', array('%file' => $file)), WATCHDOG_WARNING);
+  }
+  if (!is_file($file)) return FALSE;
+  if (exif_imagetype($file) != IMAGETYPE_JPEG) return FALSE;
+
+  return $file;
+}
+
+function _exif_DMS2D($entry) {
+  $value = $entry->getValue();
+  $degrees = $value[0][0] / $value[0][1];
+  $minutes = $value[1][0] / $value[1][1];
+  $seconds = $value[2][0] / $value[2][1];
+
+  return $degrees + $minutes/60 + $seconds/3600;
+}
+
+function _exif_fill_node_fields(&$node) {
+/*
+** As we only care about location right now, don't bother if
+** that module is installed or the location seems filled.
+*/
+
+  // Don't bother is location is not installed.
+  if (!module_exists('location')) return FALSE;
+  // Don't bother if we don't save locations for image
+  if (variable_get('location_maxnum_image', 0) < 1) return FALSE;
+  // Don't bother is this node's location is already set.
+  if (is_array($node->locations) &&
+      $node->locations[0]['latitude'] != 0) return FALSE;
+
+  $file = _exif_node_file($node);
+  if (!$file) return;
+
+  $jpeg = new PelJpeg($file);
+  $exif = $jpeg->getExif();
+  if (!$exif) return FALSE;
+  $tiff = $exif->getTiff();
+  if (!$tiff) return FALSE;
+  $ifd0 = $tiff->getIfd();
+  if (!$ifd0) return FALSE;
+  $gps = $ifd0->getSubIfd(PelIfd::GPS);
+  if (!$gps) return FALSE;
+
+  $entry = $gps->getEntry(PelTag::GPS_LATITUDE);
+  if (!$entry) return FALSE;
+  $GPS_LATITUDE = _exif_DMS2D($entry);
+  $entry = $gps->getEntry(PelTag::GPS_LATITUDE_REF);
+  if (!$entry) return FALSE;
+  if ($entry->getValue() == 'S') $GPS_LATITUDE = -$GPS_LATITUDE;
+
+  $entry = $gps->getEntry(PelTag::GPS_LONGITUDE);
+  if (!$entry) return FALSE;
+  $GPS_LONGITUDE = _exif_DMS2D($entry);
+  $entry = $gps->getEntry(PelTag::GPS_LONGITUDE_REF);
+  if (!$entry) return FALSE;
+  if ($entry->getValue() == 'W') $GPS_LONGITUDE = -$GPS_LONGITUDE;
+
+  $node->locations[0]['latitude'] = $GPS_LATITUDE;
+  $node->locations[0]['longitude'] = $GPS_LONGITUDE;
+  $node->locations['lat'] = $GPS_LATITUDE;
+  $node->locations['lon'] = $GPS_LONGITUDE;
+
+  return TRUE;
+}
+
 /**
  * Implementation of hook_nodeapi().
  */
@@ -36,11 +107,9 @@ function exif_nodeapi(&$node, $op, $teas
   switch ($op) {
     case 'view':
       // Access the file's EXIF data. Simply break when no EXIF data is found.
-      $file = file_create_path($node->images['_original']);
-      if (!file_exists($file)) {
-        watchdog('exif', t('Image %file not found.', array('%file' => $file)), WATCHDOG_WARNING);
-      }
-      if (!is_file($file)) break;
+      $file = _exif_node_file($node);
+      if (!$file) break;
+
       $jpeg = new PelJpeg($file);
       $exif = $jpeg->getExif();
       if (!$exif) break;
@@ -57,7 +126,7 @@ function exif_nodeapi(&$node, $op, $teas
       }
 
       // Retrieve the desired tag values from the file
-      $tags = exif_get_enabled_tags();
+      $tags = _exif_get_enabled_tags();
       $rows = array();
       foreach ($tags as $tag) {
         if (isset($ifds[$tag->ifd])) {
@@ -88,14 +157,21 @@ function exif_nodeapi(&$node, $op, $teas
         );
       }
       break;
-  }
+
+/* For updates, extract any info from EXIF and save if none already given */
+
+    case 'submit':
+    case 'update':
+      _exif_fill_node_fields($node);
+      break;
+    }
 }
 
 /**
  * Administration page callback.
  */
 function exif_admin_settings() {
-  $tags = exif_load_settings();
+  $tags = _exif_load_settings();
   return drupal_get_form('exif_admin_settings_form', $tags);
 }
 
@@ -126,6 +202,53 @@ function exif_admin_settings_form($tags)
     $form['tags']["{$tag->ifd}_{$tag->tag}"]['#tree'] = TRUE;
     $form['tags']["{$tag->ifd}_{$tag->tag}"]['#weight'] = $tag->weight;
   }
+
+  $form['harvest'] = array(
+    '#type' => 'fieldset',
+    '#title' => t('Node Completion'),
+    '#collapsible' => TRUE,
+    '#collapsed' => TRUE,
+    '#description' => t('When creating image nodes, what fields can be automatically harvested from the EXIF tags if not entered by the user.'),
+  );
+
+  /* The idea is that more than just location can be used in the future */
+  $form['harvest']['location'] = array(
+    '#type' => 'checkbox', 
+    '#title' => t('Location'),
+    '#default_value' => variable_get('exif_harvest_location', 0), 
+    '#description' => sprintf(t('From GPS co-ordinates. Requires Location module (%s).'),
+                      module_exists('location') ? t('<span class="admin-enabled">enabled</span>')
+                                                : t('<span class="admin-disabled">disabled/missing</span>')),
+    '#disabled' => !module_exists('location')
+  );
+
+
+  /* Figure out the scan status */
+  $scan_to = variable_get('exif_scan_to', 0);
+  $scan_last = variable_get('exif_scan_last', 0);
+
+  if ($scan_last == $scan_to) {
+    $scan_status = sprintf(t("Finished (scanned to %d)"), $scan_to);
+  } else if ($scan_last < 1 && $scan_to > 0) {
+    $scan_status = sprintf(t("Not started (%d of %d)"), $scan_last, $scan_to);
+  } else if ($scan_last < $scan_to) {
+    $scan_status = sprintf(t("In progress (%d of %d)"), $scan_last, $scan_to);
+  } else {
+    $scan_status = t('Not started');
+  }
+
+  $form['harvest']['rescan'] = array(
+    '#type' => 'markup',
+    '#value' => t('<p>The Exif module can re-scan images (done as part of cron processing) and perform node completion functions based on the above rules. This is useful when the module is first installed.</p>')
+  );
+
+  $form['harvest']['start'] = array(
+    '#type' => 'checkbox', 
+    '#title' => t('Start Scan'),
+    '#default_value' => FALSE, 
+    '#description' => t('Check this box and submit to start scan. Status is currently: ') . $scan_status
+  );
+
   $form['buttons']['submit'] = array(
     '#type' => 'submit',
     '#value' => t('Save configuration')
@@ -142,11 +265,11 @@ function exif_admin_settings_form_submit
   $op = isset($_POST['op']) ? $_POST['op'] : '';
 
   if ($op == t('Reset to defaults')) {
-    exif_reset_settings();
+    _exif_reset_settings();
     drupal_set_message(t('The configuration options have been reset to their default values.'));
   }
   elseif ($op == t('Save configuration')) {
-    exif_save_settings($values);
+    _exif_save_settings($values);
     drupal_set_message(t('The configuration options have been saved.'));
   }
 }
@@ -154,7 +277,7 @@ function exif_admin_settings_form_submit
 /**
  * Return an array containing only the tags that were enabled.
  */
-function exif_get_enabled_tags() {
+function _exif_get_enabled_tags() {
   static $tags = array();
 
   if (!count($tags)) {
@@ -164,7 +287,7 @@ function exif_get_enabled_tags() {
     }
     if (!count($tags)) {
       // Table is empty, get some defaults
-      $tags = exif_get_default_settings();
+      $tags = _exif_get_default_settings();
       foreach ($tags as $key => $tag) {
         if (!$tag->status) {
           unset($tags[$key]);
@@ -180,8 +303,8 @@ function exif_get_enabled_tags() {
 /**
  * Return an array with all the valid tags and their settings.
  */
-function exif_load_settings() {
-  $tags = exif_get_default_settings();
+function _exif_load_settings() {
+  $tags = _exif_get_default_settings();
   
   $result = db_query('SELECT * FROM {exif}');
   while ($tag = db_fetch_object($result)) {
@@ -192,10 +315,19 @@ function exif_load_settings() {
   return $tags;
 }
 
-function exif_save_settings($values) {
+function _exif_save_settings($values) {
   db_lock_table('exif');
-  foreach ($values as $tag) {
+  foreach ($values as $name => $tag) {
     if (!is_array($tag) || !isset($tag['ifd'])) {
+      /* Handle 'standard' options */
+      if ($name == 'location') {
+        variable_set('exif_harvest_location', $tag);        
+      } else if ($name = 'start' && $tag) {
+        /* Reset vars to restart scan */
+        variable_set('exif_scan_last', 0);        
+        variable_set('exif_scan_from', 1);        
+        variable_set('exif_scan_to', 3216);        
+      }
       continue; // Save only appropriate form values
     }
     db_query('DELETE FROM {exif} WHERE ifd = %d AND tag = %d', $tag['ifd'], $tag['tag']);
@@ -204,15 +336,15 @@ function exif_save_settings($values) {
   db_unlock_tables();
 }
 
-function exif_reset_settings() {
+function _exif_reset_settings() {
   db_query('DELETE FROM {exif}');
 }
 
 /**
  * Return an array with all the valid tags, with some useful default settings.
  */
-function exif_get_default_settings() {
-  $tags = exif_get_valid_tags();
+function _exif_get_default_settings() {
+  $tags = _exif_get_valid_tags();
   $tags[PelIfd::EXIF .'_'. PelTag::DATE_TIME_ORIGINAL]->status = 1;
   $tags[PelIfd::EXIF .'_'. PelTag::DATE_TIME_ORIGINAL]->weight = -10;
   $tags[PelIfd::IFD0 .'_'. PelTag::MODEL]->status = 1;
@@ -234,7 +366,7 @@ function exif_get_default_settings() {
  * For convenience, each tag has a key in the form: "ifd_tag", where ifd and tag
  * are standard Exif ids. Those ids can be found in PelIfd.php and PelTag.php.
  */
-function exif_get_valid_tags() {
+function _exif_get_valid_tags() {
   $valid_tags = array();
   $valid_tags = array_merge($valid_tags, _exif_get_valid_ifd_tags(PelIfd::IFD0, new PelIfd(PelIfd::IFD0)));
   $valid_tags = array_merge($valid_tags, _exif_get_valid_ifd_tags(PelIfd::EXIF, new PelIfd(PelIfd::EXIF)));
@@ -293,3 +425,32 @@ function theme_exif_admin_settings_form(
   return $output;
 }
 
+/**
+ * Cron - rescan images for location info if we've been directed to.
+ */
+
+function exif_cron() {
+  /* Don't bother if we're done or not started */
+  $scan_to = variable_get('exif_scan_to', 0);
+  $scan_last = variable_get('exif_scan_last', 0);
+
+  if ($scan_to == 0 or $scan_last >= $scan_to) return;
+
+  /* Continue where we left off - or start 'from' if not done before */
+  if ($scan_last == 0) $scan_last = variable_get('exif_scan_from', 0);
+
+  /* Don't be too greedy with the execution time - take one quarter */
+  $time_limit = time() + ini_get("max_execution_time") / 4;
+
+  while ($scan_last++ < $scan_to && time() < $time_limit) {
+    $node = node_load($scan_last);
+    /* If no node loaded, or not an image we don't care */
+    if ($node->nid && $node->type == 'image') {
+      /* Save if we filled anything */
+      if (_exif_fill_node_fields($node)) {
+        location_nodeapi($node, "update");
+      }
+    }
+    variable_set('exif_scan_last', $scan_last);
+  }
+}
