Skip to content

Commit

Permalink
Merge pull request #3 from josemoliver/2024updates
Browse files Browse the repository at this point in the history
After some time, an update.
  • Loading branch information
josemoliver authored Dec 29, 2024
2 parents ca596a0 + 999159d commit 6c77daf
Show file tree
Hide file tree
Showing 4 changed files with 87 additions and 75 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ There are two pieces of information required for Weather Tag to perform its func
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.
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. Use the **-overwrite** flag to overwrite existing image files, otherwise a backup of the original image files will be made.

From the Windows Command line, navigate to a folder containing the images you wish to tag as well as the weathehistory.csv file. Enter the following command:

Expand All @@ -37,7 +37,7 @@ Within the **example** folder there are 5 sample images along with a historical
You can download WeatherTag.exe from the project's GitHub Release page - https://github.com/josemoliver/WeatherTag/releases

## Build WeatherTag.exe
1. Open the **WeatherTag.sln file** in Visual Studio 2017.
1. Open the **WeatherTag.sln file** in Visual Studio 2022.
2. WeatherTag uses Newtonsoft.JSON Nuget Package which should be downloaded using the Nuget Package Manager.
3. Build Solution
4. The **WeatherTag.exe** file should be deposited in the **bin** folder.
Expand Down
149 changes: 80 additions & 69 deletions WeatherTag/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,90 +4,94 @@
using System.Linq;
using System.IO;
using Newtonsoft.Json;
using System.Text.RegularExpressions;

namespace WeatherTag
{
class Program
{

//**
//* Author: José Oliver-Didier
//* Created: October 2018
//* Ref: https://github.com/josemoliver/WeatherTag
//**

static void Main(string[] args)
{

bool WriteToFile = false;
string ActiveFilePath = Directory.GetCurrentDirectory();
List<WeatherReading> reading = new List<WeatherReading>();
bool writeToFile = false; //flag to perform write operation to save values to image metadata.
bool overwriteFile = false;
string activeFilePath = Directory.GetCurrentDirectory(); //current file directory.
List<WeatherReading> reading = new List<WeatherReading>(); //weather reading values.


//Check for Exiftool - if not found display error message.
double ExiftoolVersion = CheckExiftool();
double exiftoolVersion = CheckExiftool();

if (ExiftoolVersion!=0)
if (exiftoolVersion!=0)
{
Console.WriteLine("Exiftool version " + ExiftoolVersion);
Console.WriteLine("Exiftool version " + exiftoolVersion);
}
else
{
Console.WriteLine("Exiftool not found! WeatherTag needs exiftool in order to work properly.");
Environment.Exit(0);
}

//Fetch jpg files in directory, if none found display error
string[] ImageFiles = Directory.GetFiles(ActiveFilePath, "*.jpg");
//Fetch jpg and heic files in directory, if none found display error
string[] imageFiles = Directory.GetFiles(activeFilePath, "*.*").Where(file => file.EndsWith(".jpg", StringComparison.OrdinalIgnoreCase) || file.EndsWith(".jpeg", StringComparison.OrdinalIgnoreCase) || file.EndsWith(".heic", StringComparison.OrdinalIgnoreCase)).ToArray();

if (ImageFiles.Count()==0)
if (imageFiles.Count()==0)
{
Console.WriteLine("No .jpg files found.");
Console.WriteLine("No supported image files found.");
Environment.Exit(0);
}

string WeatherHistoryFile = Directory.GetCurrentDirectory() + "\\weatherhistory.csv";
string weatherHistoryFile = Directory.GetCurrentDirectory() + "\\weatherhistory.csv"; //Get weather history csv file


//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;
writeToFile = true;
}

if (args[i].ToString().ToLower().Trim() == "-overwrite")
{
overwriteFile = true;
}
}

if (WriteToFile==false)
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 stream
try
{
using (var reader = new StreamReader(WeatherHistoryFile))
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());
DateTime dateWeatherReading = 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());
ambientTemperature = Double.Parse(RemoveNonNumeric(values[2].ToString().Trim()));

//Check for valid Ambient Temperature Range in Celsius
if ((ambientTemperature < -100) || (ambientTemperature > 150))
Expand All @@ -104,7 +108,7 @@ static void Main(string[] args)
//Load Humidity value, if invalid mark as invalid = 99999
try
{
humidity = Double.Parse(values[3].ToString().Trim());
humidity = Double.Parse(RemoveNonNumeric(values[3].ToString().Trim()));

//Check for valid Humidity Range
if ((humidity < 0) || (humidity > 100))
Expand All @@ -119,7 +123,7 @@ static void Main(string[] args)
}
try
{
pressure = Double.Parse(values[4].ToString().Trim());
pressure = Double.Parse(RemoveNonNumeric(values[4].ToString().Trim()));

//Check for valid Pressure Range
if ((pressure < 800) || (pressure > 1100))
Expand All @@ -132,104 +136,105 @@ static void Main(string[] args)
pressure = 99999;
}

reading.Add(new WeatherReading(Date1, ambientTemperature, humidity, pressure));
reading.Add(new WeatherReading(dateWeatherReading, ambientTemperature, humidity, pressure));
}
}
}
catch
catch (Exception e)
{
Console.WriteLine(WeatherHistoryFile +" not found or unable to open.");
Console.WriteLine(weatherHistoryFile + " not found or unable to open.");
Console.WriteLine(e.ToString());
Environment.Exit(0);
}


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

DateTime PhotoDate;
bool NoPhotoDate = false;
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]);
photoDate = GetFileDate(imageFiles[q]);
}
catch
{
PhotoDate = DateTime.Parse("1/1/2050 12:00 AM");
NoPhotoDate = true;
photoDate = DateTime.Parse("1/1/2050 12:00 AM");
noPhotoDate = true;
}

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

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


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

if (MinDiffTime < 30)
if (minDiffTime < 30)
{

string ConsoleOutput = "";
string consoleOutput = "";

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

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

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


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

}
else
{
if (NoPhotoDate == true)
if (noPhotoDate == true)
{
Console.WriteLine(ImageFiles[q].ToString().Replace(Directory.GetCurrentDirectory(), "").Trim() + " - Photo file has no date and time.");
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(imageFiles[q].ToString().Replace(Directory.GetCurrentDirectory(), "").Trim() + " - No reading found.");
}
}
}
Expand All @@ -238,14 +243,15 @@ static void Main(string[] args)

}

static string RemoveNonNumeric(string input) { return Regex.Replace(input, @"[^0-9\.\-]", ""); }

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

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

// Start Process
Process p = new Process();
Expand All @@ -266,14 +272,14 @@ public static DateTime GetFileDate(string file)
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;
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);
return DateTime.Parse(createDateTime);
}

public static double CheckExiftool()
Expand Down Expand Up @@ -310,7 +316,7 @@ public static double CheckExiftool()
return exiftoolreturn;
}

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

Expand All @@ -332,6 +338,11 @@ public static string WriteFileInfo(string File, WeatherReading reading)
{
Arguments = Arguments + " -\"Pressure=" + reading.Pressure + "\"";
}
if (overwriteFile==true)
{
Arguments = Arguments + " -overwrite_original";
}


// Redirect the output stream of the child process.
p.StartInfo.UseShellExecute = false;
Expand Down
7 changes: 4 additions & 3 deletions WeatherTag/WeatherTag.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,10 @@
<OutputType>Exe</OutputType>
<RootNamespace>WeatherTag</RootNamespace>
<AssemblyName>WeatherTag</AssemblyName>
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
<TargetFrameworkVersion>v4.8</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<TargetFrameworkProfile />
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
Expand All @@ -32,8 +33,8 @@
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="Newtonsoft.Json, Version=11.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<HintPath>..\packages\Newtonsoft.Json.11.0.2\lib\net45\Newtonsoft.Json.dll</HintPath>
<Reference Include="Newtonsoft.Json, Version=13.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<HintPath>..\packages\Newtonsoft.Json.13.0.3\lib\net45\Newtonsoft.Json.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
Expand Down
2 changes: 1 addition & 1 deletion WeatherTag/packages.config
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Newtonsoft.Json" version="11.0.2" targetFramework="net461" />
<package id="Newtonsoft.Json" version="13.0.3" targetFramework="net48" />
</packages>

0 comments on commit 6c77daf

Please sign in to comment.