Hi guys,

I have cooked up this small module that works in conjunction with location.module and PHP's exif library to automatically fill the lat/lon fields.
Really handy for users with GMap module wanting to display some geocoded images.

I'll personally continue with updates to this as I'm using it for a project. It would be great to get this included with Image!

Thanks a lot.

Comments

drewish’s picture

how about != for clarity?

if (!$node->type == 'image') {

Is this still true with the latest releases? It should be updating them as it goes...

/* $node->images is out of date as image.module calls image_insert()
+	 to move the image from the temp directory to the final location before
+	 we get a chance to read the Exif data.
+	 We always call image_load() (as opposed to just on insert) to update
+	 the $node->images array as the user could upload a replacement image.
+       */

Why strlen? couldn't you just use if (!trim()) ?

if (!strlen(trim($node->locations[$index]['latitude'])) && !strlen(trim($node->locations[$index]['longitude']))) {

So this uses the pecl module?

// Exif GPS tags: http://search.cpan.org/~exiftool/Image-ExifTool-6.90/lib/Image/ExifTool/TagNames.pod#GPS_Tags
if ($exif = exif_read_data($image, 'GPS')) {

If so it really needs to be documented and I'd like to see a hook_enalble in the .install file that posts a warning if the pecl module isn't installed. Otherwise it'll be a support nightmare with people wanting to know why it doesn't work.

I'm sort of torn. It seems useful but because of the library requirement I wonder how many people would be able to use it.

drewish’s picture

Status: Needs review » Needs work
+  if (isset($value)) {
+    eval("\$result = 1.0*$value;");
+  }

oh sweet jesus you're calling eval() for this? seriously? eval is evil

lewiz’s picture

No, it uses PHP Exif stuff, i.e. PHP configured with --enable-exif. The link was to a list of all of the GPS fields used for lon/lat info.

I'll tidy up the bits you pointed out and add an install file to check for the exif module -- first time I've done this so I'm not sure what the procedure is ;)

drewish’s picture

cool, well first off get rid of that eval!

take a look at hook_enable. it needs to go in a .install file.
i guess you could call method_exists() to see if the function is defined. you can report back to the user using drupal_set_message()

also, throw in a README.txt explaining what the module is and does, how to enable it, etc.

lewiz’s picture

Status: Needs work » Needs review

Updated patch. This fixes the bits you pointed out.

I didn't mention earlier that current wonky stuff does happen with the $node->locations data. It currently is necessary to call image_load() before reading the Exif data.

The image_location.install does a check for exif_read_data(); if it can't find it, it notifies the user and automatically disables the module.

Bed for me now. Thanks :)

lewiz’s picture

StatusFileSize
new5.27 KB

ffs, here's an actual patch :P

lewiz’s picture

Please change line 21 to:

if (($node->type != 'image') || (!variable_get('location_maxnum_image', 0))) {

This makes sure that locations have been enabled for the image node type.

Thanks.

scott falconer’s picture

I think the conversion may not be negating S and W coordinates... 84°W should end up as -84. I'm not sure how to do this myself...I took a look at how wikipedia does it here and the code is:

$latfactor = 1.0 ;
		$lonfactor = 1.0 ;
		if (strtoupper($latNS) == "S") {
			$latfactor = -1.0 ;
			#$this->latdeg = -$this->latdeg;
		}

		if (strtoupper($lonEW) == "W") {
			$lonfactor = -1.0 ;
			#$this->londeg = -$this->londeg;
		}

Other than that, it seems to work great.

scott falconer’s picture

Ok, I think I got the S and W coordinates to work..not sure on how legit my code is, but it gets it done. If anyone has any more advice on making this a bit cleaner, let me know...

Starting and line 45:

foreach ($node->locations as $index => $location) {
	// check if the user has manually entered a lat/lon
	if (!trim($node->locations[$index]['latitude']) && !trim($node->locations[$index]['longitude'])) {
	  // attempt to populate locations with Exif info
	  // Exif GPS tags: http://search.cpan.org/~exiftool/Image-ExifTool-6.90/lib/Image/ExifTool/TagNames.pod#GPS_Tags
	  if ($exif = exif_read_data($image, 'GPS')) {
	    if (isset($exif['GPSLatitude']) && isset($exif['GPSLongitude'])) {
	      // FIXME Future work: proper validity checking
	    if (strtoupper($exif['GPSLongitudeRef']) == "W") {
	      $node->locations[$index]['longitude'] = "-" . _image_location_dms_to_dd($exif['GPSLongitude']);
	   }
	   
	   else { 
	      $node->locations[$index]['longitude'] =  _image_location_dms_to_dd($exif['GPSLongitude']);
	      }
	      
	      if (strtoupper($exif['GPSLatitudeRef']) == "S") {
	      $node->locations[$index]['latitude'] = "-" . _image_location_dms_to_dd($exif['GPSLatitude']);
	   }
	   else { 
	      $node->locations[$index]['latitude'] = _image_location_dms_to_dd($exif['GPSLatitude']);
	      }

	   }
	  }
	}
      }
    break;
  }
}

raintonr’s picture

Subscribing.

Sounds useful. Could/should this be added to the Exif module somehow though? I'd be interested in seeing something that scans all existing images for when this is first installed.

raintonr’s picture

I couldn't see where the patches on this thread were supposed to fit so hacked about with the EXIF.module.

Add this into the case in exif_nodeapi. Note that there is already a check in that code to ignore anything but images so it wasn't needed.

* For updates, extract the location from EXIF and save if none already given */

    case 'update':
      // Don't bother if no locations in array
      if (!array_key_exists('location', $node)) break;

      // Don't bother if location already set
      if ($node->location['lat'] != 0) break;

      // 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;

      $jpeg = new PelJpeg($file);
      $exif = $jpeg->getExif();
      if (!$exif) break;
      $tiff = $exif->getTiff();
      if (!$tiff) break;
      $ifd0 = $tiff->getIfd();
      if (!$ifd0) break;
      $gps = $ifd0->getSubIfd(PelIfd::GPS);
      if (!$gps) break;

      function _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;
      }

      $entry = $gps->getEntry(PelTag::GPS_LATITUDE);
      if (!$entry) break;
      $GPS_LATITUDE = _DMS2D($entry);
      $entry = $gps->getEntry(PelTag::GPS_LATITUDE_REF);
      if (!$entry) break;
      if ($entry->getValue() == 'S') $GPS_LATITUDE = -$GPS_LATITUDE;

      $entry = $gps->getEntry(PelTag::GPS_LONGITUDE);
      if (!$entry) break;
      $GPS_LONGITUDE = _DMS2D($entry);
      $entry = $gps->getEntry(PelTag::GPS_LONGITUDE_REF);
      if (!$entry) break;
      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;
      break;

Works here. Could do with more checking of course and perhaps some cleanup, but think this is a more appropriate place for this kind of code.

mawds’s picture

Version: 6.x-1.x-dev » 5.x-1.7
Category: feature » bug

The module doesn't work with image_import:

* warning: Invalid argument supplied for foreach() in /htdocs/modules/image/contrib/image_location/image_location.module on line 46.
* warning: Invalid argument supplied for foreach() in /mawdshome/htdocs/modules/location/location.module on line 857.

Can anyone suggest a workaround/bug fix?

Thanks

elyobo’s picture

I'd also be very interested in using it if it could be made to work with the image import module. Sounds like a great idea :)

rubenk’s picture

bump. i too would like to see this integrated with all the upcoming geotagging support from things like the iphone and other mobile phones.

elyobo’s picture

Quick code snippet for reading GPS data from EXIF information... It appears that it's stored in degrees, minutes, seconds format, with the value stored as a fraction, e.g. 121.22 would be stored as 12122/100. The following snippet converts the DMS format to seconds after extracting it from the GPS data, if you don't happen to have the EXIF module to do your converting for you (EXIF requires PEL requires PHP5, which my server isn't running...).

function doEquation($e) {
  $p = split("/",$e);
  return $p[0]/$p[1];
}

function dmsToDecimal($c,$r) {
  if(!is_array($c)) {
    return;
  }
  $val = doEquation($c[0]) + doEquation($c[1])/60 + doEquation($c[2])*100/60;
  if(ucwords($r) == "S" || ucwords($r) == "W") {
    $val *= -1;
  }
  return $val;
}

$filename = "rimg0166.jpg";
$res = exif_read_data($filename, "GPS");
$lat = dmsToDecimal($res[GPSLatitude], $res[GPSLatitudeRef]);
$long = dmsToDecimal($res[GPSLongitude], $res[GPSLongitudeRef]);

print "$lat,$long\n";

I'll look into integrating this into the code. I'm working with D5, so probably won't be supplying any patches for D6/D7.

elyobo’s picture

Bah, I see that the original patch already included this functionality. Ignore the above.

elyobo’s picture

elyobo’s picture

StatusFileSize
new5.99 KB

I've modified the patch so that it
- deals with N and W correctly (negative decimal numbers)
- works with image import - this requires a trivial change to image.module

See the attached patch.

In order for the image import to work, the location assignment has been split into a second function which is called from the image module (if image_location is enabled). To activate it, the following lines need to be added into image_create_node_from in image.module.

  if(module_exists("image_location")) {
    add_exif_location($node, $filepath);
  }

This should be added beneath the section assigning the taxonomy, i.e. beneath this snippet

  if (module_exists('taxonomy')) { 
    $node->taxonomy = $taxonomy;
  }
sun’s picture

Status: Needs review » Closed (won't fix)

Location module should integrate with Image module, not the other way around.

summit’s picture

Hi, Would be great if this would be integrated in location 3.dev!
Greetings, Martijn

zzolo’s picture

The Exif modules does this well, and has a supplemental exif_location module that does exactly all this. I have not tested this out, but it would be a good place to look.