diff --git a/README.md b/README.md index 179ba55..a5c7f39 100644 --- a/README.md +++ b/README.md @@ -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: @@ -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. diff --git a/WeatherTag/Program.cs b/WeatherTag/Program.cs index f4d8815..7cc4b92 100644 --- a/WeatherTag/Program.cs +++ b/WeatherTag/Program.cs @@ -4,26 +4,33 @@ 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 reading = new List(); + 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 reading = new List(); //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 { @@ -31,16 +38,16 @@ static void Main(string[] args) 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. @@ -48,46 +55,43 @@ static void Main(string[] args) { 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)) @@ -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)) @@ -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)) @@ -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."); } } } @@ -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 ExifToolResponse; - string CreateDateTime = ""; - string CreateDate = ""; - string CreateTime = ""; + string createDateTime = ""; + string createDate = ""; + string createTime = ""; // Start Process Process p = new Process(); @@ -266,14 +272,14 @@ public static DateTime GetFileDate(string file) if (json != "") { ExifToolResponse = JsonConvert.DeserializeObject>(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() @@ -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 @@ -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; diff --git a/WeatherTag/WeatherTag.csproj b/WeatherTag/WeatherTag.csproj index de67d7a..aa5f670 100644 --- a/WeatherTag/WeatherTag.csproj +++ b/WeatherTag/WeatherTag.csproj @@ -8,9 +8,10 @@ Exe WeatherTag WeatherTag - v4.6.1 + v4.8 512 true + AnyCPU @@ -32,8 +33,8 @@ 4 - - ..\packages\Newtonsoft.Json.11.0.2\lib\net45\Newtonsoft.Json.dll + + ..\packages\Newtonsoft.Json.13.0.3\lib\net45\Newtonsoft.Json.dll diff --git a/WeatherTag/packages.config b/WeatherTag/packages.config index 5762754..0b14af3 100644 --- a/WeatherTag/packages.config +++ b/WeatherTag/packages.config @@ -1,4 +1,4 @@  - + \ No newline at end of file