diff --git a/utility/exif/94-exif.html b/utility/exif/94-exif.html
new file mode 100644
index 00000000..689c290c
--- /dev/null
+++ b/utility/exif/94-exif.html
@@ -0,0 +1,49 @@
+
+
+
+
+
+
+
diff --git a/utility/exif/94-exif.js b/utility/exif/94-exif.js
new file mode 100644
index 00000000..eb7140c7
--- /dev/null
+++ b/utility/exif/94-exif.js
@@ -0,0 +1,124 @@
+/**
+ * Copyright 2014 IBM Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ **/
+
+module.exports = function(RED) {
+ "use strict";
+ var ExifImage = require('exif').ExifImage;
+
+ function convertDegreesMinutesSecondsToDecimals(degrees, minutes, seconds) {
+ var result;
+ result = degrees + (minutes / 60) + (seconds / 3600);
+ return result;
+ }
+
+ /***
+ * Extracts GPS location information from Exif data. If enough information is
+ * provided, convert the Exif data into a pair of single floating point number
+ * latitude/longitude data pairs. Populates msg.location with these.
+ * Assumes that the msg object will always have exifData available as msg.exif.
+ * Assume that the GPS data saved into Exif provides a valid value
+ */
+ function addMsgLocationDataFromExifGPSData(msg) {
+ if(msg.exif.gps) {
+ var gpsData = msg.exif.gps; // declaring variable purely to make checks more readable
+ if(gpsData.GPSAltitude) {
+ if(!msg.location) {
+ msg.location = {};
+ }
+ msg.location.alt = gpsData.GPSAltitude;
+ }
+ if(gpsData.GPSLatitudeRef && gpsData.GPSLatitude && gpsData.GPSLongitudeRef && gpsData.GPSLongitude) { // location can be determined, OK
+ // The data provided in Exif is in degrees, minutes, seconds, this is to be converted into a single floating point degree
+ if(gpsData.GPSLatitude.length === 3) { // OK to convert latitude
+ if(gpsData.GPSLongitude.length === 3) { // OK to convert longitude
+ var latitude = convertDegreesMinutesSecondsToDecimals(gpsData.GPSLatitude[0], gpsData.GPSLatitude[1], gpsData.GPSLatitude[2]);
+ latitude = Math.round(latitude * 100000)/100000;
+
+ // (N)orth means positive latitude
+ // (S)outh means negative latitude
+ // (E)ast means positive longitude
+ // (W)est means negative longitude
+ if(gpsData.GPSLatitudeRef.toString() === 'S' || gpsData.GPSLatitudeRef.toString() === 's') {
+ latitude = latitude * -1;
+ }
+
+ var longitude = convertDegreesMinutesSecondsToDecimals(gpsData.GPSLongitude[0], gpsData.GPSLongitude[1], gpsData.GPSLongitude[2]);
+ longitude = Math.round(longitude * 100000)/100000;
+
+ if(gpsData.GPSLongitudeRef.toString() === 'W' || gpsData.GPSLongitudeRef.toString() === 'w') {
+ longitude = longitude * -1;
+ }
+ if(!msg.location) {
+ msg.location = {};
+ }
+ msg.location.lat = latitude;
+ msg.location.lon = longitude;
+ return;
+ } else {
+ node.log("Invalid longitude data, no location information has been added to the message.");
+ }
+ } else {
+ node.log("Invalid latitude data, no location information has been added to the message.");
+ }
+ } else {
+ node.log("The location of this image cannot be determined safely so no location information has been added to the message.");
+ }
+ } else {
+ node.log("No location info found in this image.");
+ }
+ }
+
+ function ExifNode(n) {
+ RED.nodes.createNode(this,n);
+ var node = this;
+ this.on("input", function(msg) {
+ try {
+
+ if(msg.payload) {
+ var isBuffer = Buffer.isBuffer(msg.payload);
+ if(isBuffer === true) {
+ new ExifImage({ image : msg.payload }, function (error, exifData) {
+ if (error) {
+ node.error('Failed to extract Exif data from image.');
+ node.log('Failed to extract Exif data from image. '+ error);
+ } else {
+ //msg.payload remains the same buffer
+ if(exifData) {
+ msg.exif = exifData;
+ addMsgLocationDataFromExifGPSData(msg);
+ } else {
+ node.warn("The incoming buffer did not contain Exif data, nothing to do. ");
+ }
+ }
+ node.send(msg);
+ });
+ } else {
+ node.error("Invalid payload received, the Exif node cannot proceed, no messages sent.");
+ return;
+ }
+ } else {
+ node.error("No payload received, the Exif node cannot proceed, no messages sent.");
+ return;
+ }
+ } catch (error) {
+ node.error("An error occurred while extracting Exif information. Please check the log for details.");
+ node.log('Error: '+error.message);
+ return;
+ }
+ });
+ }
+ RED.nodes.registerType("exif",ExifNode);
+}
diff --git a/utility/exif/package.json b/utility/exif/package.json
new file mode 100644
index 00000000..ba12c7d0
--- /dev/null
+++ b/utility/exif/package.json
@@ -0,0 +1,23 @@
+{
+ "name" : "node-red-node-exif",
+ "version" : "0.0.1",
+ "description" : "A Node-RED node that extracts Exif information from JPEG image buffers.",
+ "dependencies" : {
+ "exif": "0.4.0"
+ },
+ "repository" : {
+ "type":"git",
+ "url":"https://github.com/node-red/node-red-nodes/tree/master/utility/exif"
+ },
+ "license": "Apache-v2.0",
+ "keywords": [ "node-red", "exif"],
+ "node-red" : {
+ "nodes" : {
+ "exif": "94-exif.js"
+ }
+ },
+ "contributors": [
+ {"name": "Dave Conway-Jones"},
+ {"name": "Zoltan Balogh"}
+ ]
+}