Skip to content

Technical Description of File interactions

Viktor Nemeth edited this page Dec 3, 2022 · 2 revisions

First off, welcome to the Wiki - it's heavily under construction and this particular page is an initial placeholder for a semi-technical explanation of what the code does in a hybrid of laymen terms and technical.

Where does the information come from?

As to what EXIF is, that's generally detailed on Wikipedia. A TLDR of it is that media files hold various metadata, such as when the image was taken, what sort of device took it (say iPhone 14 or Nikon D5) and a myriad of other potential bits and bobs - this is stored in EXIF. EXIF has evolved over time and is a very flexible thing. Manufacturers can and do include their own tags, many of which are not standard.

With regards to GTN, we use Phil Harvey's exifTool to do the actual interaction with files, extraction and saving.

How Tags work (?)

Continuing from the last bit of logic, the reason why we use exifTool is because it's up-to-date and is being constantly developed. EXIF itself is a complicated bit of clusterf..k and as oftentimes is the case, human logic doesn't line up with code logic. EXIF generally has Tags that don't mean the same thing as what people would think of as tag. Generally speaking there are four main types/categories of Tags:

  • EXIF
  • IPTC
  • XMP
  • Composite

XMP was mostly introduced and are used by Adobe, which, while a is a common thing for photographers and workflows, it also introduces the problem that if a software isn't reading from the other possible places, or isn't writing to them then metadata within a file can become out of sync. See below.

From a user perspective these are just added levels of complications. Also there are things we people see but there are no tags for it. For example "Lens" (as in say Nikon 70-200/2.8) could possibly appear in one or more of the following list: EXIF:LensModel XMP:LensInfo Composite:Lens

The order of appearance is also important. Certain software will read/write to one but not necessarily the other of these, which is stupid, but we're all stuck on this crazy planet after all.

Also for example there is also no actual Tag for "Coordinates". Instead there are a number of tags for GPSLatitude exif:GPSLatitude GPSLatitudeRef exif:GPSLatitudeRef GPSLongitude exif:GPSLongitude GPSLongitudeRef exif:GPSLongitudeRef

There is also no "TimeZone" but there is EXIF:OffsetTimeOriginal and EXIF:OffsetTime, which are just string values of say +01:00, and then there is no way to establish without using an extra layer of complication using Coordinates (technically a combination of the 8 tags above) + TakenDate (really, 2 tags for that too) + an API service to find what actual time zone that belongs to and then calling a built-in Windows service to find if at the given time above whether the location was in DST or not. Joy. There is also no "time shift" and a bunch of other things.

So jumping back a few hoops, the reason why exifTool is used is because on top of everything else these various tags reside in files in places that I personally wouldn't want to dig out because I probably wouldn't know where to start. From what I gather there are literally tens of thousands of various possibilities for different manufacturers and models and there is no point in reinventing the wheel. ExifTool basically does that part of the job for us. If anyone is interested, read the exifTool Tags list or just find and read their lengthy documentation.

Formats are non-standard

To add insult to injury the formats are not necessarily standardised either. Depending on the camera manufacturer values can be pretty random. E.g. 35mm equivalent length (FocalLengthIn35mmFormat) - which technically could sit in any of EXIF:FocalLengthIn35mmFormat, XMP:FocalLengthIn35mmFormat, Composite:FocalLength35efl - could be just a number (81.7) or with text (81.7mm) or 51.0 mm (35 mm equivalent: 81.7 mm). Similarly some of the fractional values such as Exposure Time (EXIF:ExposureTime, XMP:ExposureTime) could be represented as a decimal (0.5) or fraction (1/2) with or without sec added to it, and so on. FML, right?

What the GTN Code does

Here comes the fun part. This a bit techier. Out of pure laziness I'll call the main ListView (lvw_FileList) "grid" (that's only 4 characters).

The grid has a bunch of columns, at the time of writing this that is about 34 columns or so. In C# .NET WinForms (the GUI/language this whole thing is written in), a ListView has an Item.Text and a number of Subitems, the prior being the first column and is compulsory, the latter being the rest of columns and technically are optional. ListView Columns can have a ColumnHeader that can/should/need to be programmatically named and in GTN they always start with "clh_". In fact every object starts with "xxx_" and the ones that relate to Tags always have the logic of "xxx_TagName". The list of Tags we want are stored in the XLSM file ExtraFiles\objectMapping.xlsm - this has two relevant sheets, one for "tags in" and the other for "tags out". Both are collations of human-to-code mappings as per above (ie for objectName = ExposureTime it will map to objectTagName_In = EXIF:ExposureTime and objectTagName_In = XMP:ExposureTime and those will have a valuePriorityOrder assigned to them.) [the Excel macro just creates an SQLite file GTN will consume]. The reasoning behind the priority separation is hinted at above.

When GTN runs it does two things to "fill the grid".

First it parses the folder for its generic contents, it basically just lists the files and subfolders. Technically this adds Items to the grid (with empty Subitems).

Then it uses exifTool to extract the ~70 tags that map to the ~35 columns. In technical terms what happens is that as of 20221202's version ExifGetExifFromFolder(string folderNameToUse) fires off a command that will instruct exifTool to take all the files in a given folder and shove all the 70 tags per file into one big CSV. Due to the nature of Windows's cmd (and its inherent inability to handle non-standard-Latin characters) this technically goes through a so-called arguments-file (both of which sit in ApplicationData\GeoTagNinja until they get deleted) then the CSV gets parsed into a DataTable.

There is an added level of complexity here because certain (but not all) files may have an extra xmp sidecar file attached to them, which may contain extra and/or contradictory information and that needs to be added as well.

The dtFileExifTable contains information at the "extended" tag (objectTagName_In) level, such as EXIF:ExposureTime and XMP:ExposureTime. For each file and tag the ExifGetStandardisedDataPointFromExif(DataTable dtFileExif,string dataPoint) fires, which basically converts the extended tag info into consumable tag (objectName) info. It loops through the various tags and either takes them at face value according to valuePriorityOrder or makes some modifications to them (see comments above for things like fractions and decimals and whatnots). For non-existing tags (such as Coordinates) that value gets created on the fly.

Strictly speaking ExifGetStandardisedDataPointFromExif will call ExifGetRawDataPointFromExif that does the mapping-extraction and takes the string value of the first non-null value in the dtFileExif for the given dataPoint according to valuePriorityOrder) and return that string to ExifGetStandardisedDataPointFromExif to doits magic.

One this is all done the code will push data back into the grid with the logic that the Column clh_xxx will take its value from objectName, i.e. clh_FocalLengthIn35mmFormat will get the value from the converted objectName of FocalLengthIn35mmFormat).

Saving Data

Saving data to the media works in a reverse logic to the above and so I won't detail it. The code takes values that are stored in a particular DataTable (DtFileDataToWriteStage3ReadyToWrite) and yet again uses exifTool (in what's pretty much a reverse of the "read" logic) to write the files.

Clone this wiki locally