Skip to content

Commit

Permalink
Initial Release
Browse files Browse the repository at this point in the history
  • Loading branch information
josemoliver committed Oct 11, 2018
1 parent a2e2cd1 commit 62e0524
Show file tree
Hide file tree
Showing 13 changed files with 758 additions and 2 deletions.
38 changes: 36 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,36 @@
# WeatherTag
A simple Windows console app for adding weather values to a set of jpg image file EXIF 2.31 Metadata. Using the date and time a photo was taken, the app matches the closest weather reading from a file containing periodic weather readings weatherhistory.csv file.
# Weather Tag

## Introduction

This is a simple Windows console (Command Line) application for adding weather values to a set of jpg image file EXIF 2.31 Metadata. Using the date and time a photo was taken, the app matches the closest weather reading from a file containing periodic weather readings weatherhistory.csv file.

The idea behind this utility is if you can obtain historical weather information for a location near where the photos were taken this information could be saved within the image file metadata.

Read the blog post: [Capturing the moment and the ambient weather information in photos](https://jmoliver.wordpress.com/2018/07/07/capturing-the-moment-and-the-ambient-weather-information-in-photos/)

## Dependency with ExifTool

WeatherTag requires that **ExifTool.exe** be copied into the same folder with WeatherTag.exe on your PC. ExifTool is a utility written by Phil Harvey which can read, write and edit file metadata. Exiftool can be obtained at https://www.sno.phy.queensu.ca/~phil/exiftool/

## Installation

1. On your PC create a folder called "WeatherTag"
2. Copy the WeatherTag.exe file to the folder WeatherTag
3. Copy Exiftool.exe to the Weather Tag folder.
4. Add the Weather Tag folder to the Windows **path** variable. If you are not familiar on how to add a folder to the Windows **path** variable. Search the web for "Add folder to Environment path" for detailed descriptions as it may vary between Windows versions.

## Usage
There are two pieces of information required for Weather Tag to perform its function:
1. Any number of photos taken with correct Date and Time stamp.
2. A comma separated value file (.csv) in UTF-8 named **weatherhistory.csv** containing periodic weather measurements. The first column values are required to contain a date in MM/DD/YYYY format (Example: 10/5/2018). The second column values are required to contain time values in 12-Hour format (Example: 3:00 PM). The third column should contain ambient temperature values in Celsius. The fourth column values should contain atmospheric humidity in percentage. The fifth column values should contain atmospheric pressure in hectopascals (hPa). The Ambient Temperature, Humidity and Pressure values need not have the measurement units added (°C, %, hPa).

When WeatherTag.exe is executed within a folder containing image files and the weatherhistory.csv file it will attempt to match the closest weather reading (within an hour) for the date and time a photo was taken. If the **-write** flag is use it will then write the Ambient Temperature, Humidity and Pressure values to the image files' corresponding EXIF metadata fields.

## Example Files
Within the **example** folder there are 5 sample images along with a historical weather log from a weather station near the location where the photos were taken. Open a Command Prompt within the example folder. Running **WeatherTag.exe** within the folder will match the closest weather measurement contained. Running **WeatherTag.exe -write** will match the weather measurements as well as write the information back to the photo image files. A copy of the original image file will be made with the ***.jpg_original** extension. If you wish to delete the original image files and keep the modified files you can delete them by using the **del *.jpg_original** command.

## Build WeatherTag.exe
1. Open the **WeatherTag.sln file** in Visual Studio 2017.
2. WeatherTag uses Newtonsoft.JSON Nuget Package which should be downloaded using the Nuget Package Manager.
3. Build Solution <Ctrl>+<Shift>+<B>
4. The **WeatherTag.exe** file should be deposited in the **bin** folder.
25 changes: 25 additions & 0 deletions WeatherTag.sln
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.26730.12
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WeatherTag", "WeatherTag\WeatherTag.csproj", "{0D6E5463-98AA-421B-97CB-7250C6864555}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{0D6E5463-98AA-421B-97CB-7250C6864555}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{0D6E5463-98AA-421B-97CB-7250C6864555}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0D6E5463-98AA-421B-97CB-7250C6864555}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0D6E5463-98AA-421B-97CB-7250C6864555}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {9A25BEFF-2A14-40D7-B25F-7AFC4053C1AD}
EndGlobalSection
EndGlobal
6 changes: 6 additions & 0 deletions WeatherTag/App.config
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.1" />
</startup>
</configuration>
307 changes: 307 additions & 0 deletions WeatherTag/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,307 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.IO;
using Newtonsoft.Json;

namespace WeatherTag
{
class Program
{

static void Main(string[] args)
{

bool WriteToFile = false;
string ActiveFilePath = Directory.GetCurrentDirectory();
List<WeatherReading> reading = new List<WeatherReading>();
string[] ImageFiles = Directory.GetFiles(ActiveFilePath, "*.jpg");
string WeatherHistoryFile = Directory.GetCurrentDirectory() + "\\weatherhistory.csv";

//Check for -write flag, if found then matched weather values will be writen back to the jpg file EXIF metadata.
for (int i=0; i<args.Count();i++)
{
if (args[i].ToString().ToLower().Trim()=="-write")
{
WriteToFile = true;
}
}

if (WriteToFile==false)
{
Console.WriteLine("No changes to file(s) will be performed - To write weather tags use -write flag");
}


Console.WriteLine("Weather file: " + WeatherHistoryFile);

//Load weather history file into memory
using (var reader = new StreamReader(WeatherHistoryFile))
{
while (!reader.EndOfStream)
{
var line = reader.ReadLine();

//Remove any measurement symbols
line = line.Replace("°", "");
line = line.Replace("C", "");
line = line.Replace("hPa", "");
line = line.Replace("%", "");

var values = line.Split(',');

Double ambientTemperature = 99999;
Double humidity = 99999;
Double pressure = 99999;

DateTime Date1 = DateTime.Parse(values[0].ToString().Trim() + " " + values[1].ToString().Trim());


//Load Ambient Temperature value, if invalid mark as invalid = 99999
try
{
ambientTemperature = Double.Parse(values[2].ToString().Trim());

//Check for valid Ambient Temperature Range in Celsius
if ((ambientTemperature<-100)||(ambientTemperature>150))
{
ambientTemperature = 99999;
}

}
catch
{
ambientTemperature = 99999;
}

//Load Humidity value, if invalid mark as invalid = 99999
try
{
humidity = Double.Parse(values[3].ToString().Trim());

//Check for valid Humidity Range
if ((humidity < 0) || (humidity > 100))
{
humidity = 99999;
}

}
catch
{
humidity = 99999;
}
try
{
pressure = Double.Parse(values[4].ToString().Trim());

//Check for valid Pressure Range
if ((pressure < 800) || (pressure > 1100))
{
pressure = 99999;
}
}
catch
{
pressure = 99999;
}

reading.Add(new WeatherReading(Date1, ambientTemperature, humidity, pressure));
}
}


//For each image file in the folder, add the closest reading from the weather history file.
for (int q = 0; q < ImageFiles.Count(); q++)
{

DateTime PhotoDate;
bool NoPhotoDate = false;

//Get the image's Created Time, if not found or an error occurs set to error value of 1/1/2050 12:00 AM
try
{
PhotoDate = GetFileDate(ImageFiles[q]);
}
catch
{
PhotoDate = DateTime.Parse("1/1/2050 12:00 AM");
NoPhotoDate = true;
}

Double MinDiffTime = 30;
WeatherReading closestReading = new WeatherReading(DateTime.Parse("1/1/1900 12:00 AM"), 0, 0, 0);

if (NoPhotoDate == false)
{
for (int i = 0; i < reading.Count; i++)
{
TimeSpan DiffTime = PhotoDate - reading[i].ReadingDate;
if (Math.Abs(DiffTime.TotalMinutes) < MinDiffTime)
{
closestReading = reading[i];
MinDiffTime = Math.Abs(DiffTime.TotalMinutes);
}
}
}


Console.WriteLine("------ File " + (q+1).ToString()+ " of " + ImageFiles.Count() + " ------");

if (MinDiffTime < 30)
{

string ConsoleOutput = "";

if (closestReading.AmbientTemperature != 99999)
{
ConsoleOutput = ConsoleOutput + " " + closestReading.AmbientTemperature.ToString() + "°C ";
}
else
{
ConsoleOutput = ConsoleOutput + " -- °C ";
}

if (closestReading.Humidity != 99999)
{
ConsoleOutput = ConsoleOutput + " " + closestReading.Humidity.ToString() + " %";
}
else
{
ConsoleOutput = ConsoleOutput + " -- % ";
}

if (closestReading.Pressure != 99999)
{
ConsoleOutput = ConsoleOutput + " " + closestReading.Pressure.ToString() + " hPa";
}
else
{
ConsoleOutput = ConsoleOutput + " -- hPa ";
}


Console.WriteLine(ImageFiles[q].ToString().Replace(Directory.GetCurrentDirectory(),"").Trim()+" - "+ConsoleOutput);
if (WriteToFile == true)
{
string WriteStatus = WriteFileInfo(ImageFiles[q], closestReading);
Console.WriteLine(WriteStatus);
}

}
else
{
if (NoPhotoDate == true)
{
Console.WriteLine(ImageFiles[q].ToString().Replace(Directory.GetCurrentDirectory(), "").Trim() + " - Photo file has no date and time.");
}
else
{
Console.WriteLine(ImageFiles[q].ToString().Replace(Directory.GetCurrentDirectory(), "").Trim() + " - No reading found.");
}
}
}

Console.WriteLine();

}

public static DateTime GetFileDate(string file)
{
//Retrieve Image Date

List<ExifToolJSON> ExifToolResponse;
string CreateDateTime = "";
string CreateDate = "";
string CreateTime = "";

// Start Process
Process p = new Process();

// Redirect the output stream of the child process.
p.StartInfo.UseShellExecute = false;
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.CreateNoWindow = true;
p.StartInfo.Arguments = "\"" + file + "\" -CreateDate -mwg -json";
p.StartInfo.FileName = "exiftool.exe";
p.Start();


// Read the output stream
string json = p.StandardOutput.ReadToEnd();
p.WaitForExit();

if (json != "")
{
ExifToolResponse = JsonConvert.DeserializeObject<List<ExifToolJSON>>(json);
CreateDateTime = ExifToolResponse[0].CreateDate.ToString().Trim();
string[] words = CreateDateTime.Split(' ');
CreateDate = words[0].ToString().Replace(":","/");
CreateTime = words[1].ToString();
CreateDateTime = CreateDate + " " + CreateTime;
}

return DateTime.Parse(CreateDateTime);
}

public static string WriteFileInfo(string File, WeatherReading reading)
{
//Write Weather Values back to file

string output = "";
// Start the child process.
Process p = new Process();

string Arguments = "";

if (reading.AmbientTemperature != 99999)
{
Arguments = Arguments + " -\"AmbientTemperature=" + reading.AmbientTemperature + "\"";
}
if (reading.Humidity != 99999)
{
Arguments = Arguments + " -\"Humidity=" + reading.Humidity + "\"";
}
if (reading.Pressure != 99999)
{
Arguments = Arguments + " -\"Pressure=" + reading.Pressure + "\"";
}

// Redirect the output stream of the child process.
p.StartInfo.UseShellExecute = false;
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.CreateNoWindow = true;
p.StartInfo.Arguments = "\""+File+"\"" + Arguments;
p.StartInfo.FileName = "exiftool.exe";
p.Start();
output = File + Arguments+" --- " + p.StandardOutput.ReadToEnd();
p.WaitForExit();

return output;
}

public class ExifToolJSON
{
public string SourceFile { get; set; }
public string CreateDate { get; set; }
}


public class WeatherReading
{
public WeatherReading(DateTime readingDate, double ambientTemperature, double humidity, double pressure)
{
this.ReadingDate = readingDate;
this.AmbientTemperature = ambientTemperature;
this.Humidity = humidity;
this.Pressure = pressure;
}

public DateTime ReadingDate { get; set; }
public double AmbientTemperature { get; set; }
public double Humidity { get; set; }
public double Pressure { get; set; }
}
}
}

Loading

0 comments on commit 62e0524

Please sign in to comment.