'Use PowerShell to extract GPS Latitude etc. (Properties > Details) from an image file
I am looking for a way to extract the GPS Latitude and Longitude values from a lot of .jpg
files.
I know that Exiftool can sort of do it, but as the values I'm after are visible in Windows 10 Explorer for that file (Properties > Detail > GPS heading > Latitude)... Can I use PS to directly grab them? I'm assuming it would be quicker that way.
I know how to extract $img.FullName
etc., but can't get to Latitude this way.
Solution 1:[1]
To access the metadata (different from filesystem metadata) you see in Windows Explorer without external tools, you have to use the Windows Image Acquisition (WIA) Automation Layer. You can do it like that:
# Create an ImageFile object and load an image file
$image = New-Object -ComObject Wia.ImageFile
$image.LoadFile("C:\Absolute\path\to\my.jpg")
# Read your desirered metadata
$image.Properties.Item('GpsLatitude').Value
$image.Properties.Item('GpsLongitude').Value
Be aware that WIA has only limited parsing capabiltites in comparison to external tools like ExifTool or exiv2. But it will be enough to get the data you need in your case.
You can read more about ImageFile
objects and what they are capapble of here.
Solution 2:[2]
Thanks to everyone, particularly @stackprotector. I've learned a lot and managed to work out how to extract my exact values, including the important N,S,E,W.
To help others, here's what I discovered:
# Based on answer from @stackprotector (above)
# Create an ImageFile object and load an image file
$image = New-Object -ComObject Wia.ImageFile
$image.LoadFile("D:\temp\toNAS\temp\PXL_20210830_004210948.jpg")
$image.Properties.Item('GpsLatitudeRef').Value
# S
$image.Properties.Item('GpsLatitude').Value
<#
Value Numerator Denominator
----- --------- -----------
37 37 1
51 51 1
43.92 4392 100
>#
# Can rinse and repeat for GpsLongitude
Now to extract what I want. This is a subset; easy to copy in GpsLongitude for the other one :-)
$image.Properties.Item('GpsLatitude').Value[1].Value
# 37
$image.Properties.Item('GpsLatitude').Value[2].Value
# 51
$image.Properties.Item('GpsLatitude').Value[3].Value
# 43.92
$image.Properties.Item('GpsLatitudeRef').Value
# S
$image.Properties.Item('DateTime').Value
# 2021:08:30 10:42:10
# This is local not UTC
# So final latitude is 37 deg 51 min 43.92 sec S
# Taken on 2021:08:30 10:42:10 local time
In-camera value says -37.8622 and taken 30 Aug 2021 at 10:42:10 local, so happy with that, including the '-' for South
I will try Exiftool @daniel to see if it's easier. I have used it before and it's excellent.
Update (2022-02-13). I DID end up going with Exiftool. As I became more confident with JSON that toolset became more obvious for me. Here's the key snippets of the code (below). One little thing: this exiftool option set gives no "feedback" when running. Only at the end does it give a summary:
# $photoYear is the root folder with the images
$data = (exiftool -if '$gpsdatetime' -s -s -s -json -ext jpg -filename -FileTypeExtension -Directory -CreateDate -GPSDateTime -GPSLatitude -GPSLongitude -n -r $photoYear ) | ConvertFrom-Json
# ...
[int]$n = 0
foreach ($photo in $data){
Write-host $n, " " $photo.CreateDate, $photo.GPSLatitude, $photo.GPSLongitude
$n++
}
Solution 3:[3]
Using @dwids and @stackprotector answers, I came up with the following to run through all files in a folder. It's not the tidiest, but it's self explanatory:
cls
$FolderPath = "c:\MyFolder"
Get-ChildItem $FolderPath -Filter *.* | where { ! $_.PSIsContainer } |
Foreach-Object {
# Get the file name and path, write it out to screen
$FileName = $_.FullName
Write-Host "$FileName"
# Create an ImageFile object and load an image file
$image = New-Object -ComObject Wia.ImageFile
$image.LoadFile($FileName)
# Read your desirered metadata, if it doesn't contain any, say NONE
try
{
#Clear variables for Lat and Lon
Clear-Variable Lat*
Clear-Variable Lon*
$LatDEG = $image.Properties.Item('GpsLatitude').Value[1].Value
$LatMIN = $image.Properties.Item('GpsLatitude').Value[2].Value
$LatSEC = $image.Properties.Item('GpsLatitude').Value[3].Value
$LatREF = $image.Properties.Item('GpsLatitudeRef').Value
$LonDEG = $image.Properties.Item('GpsLongitude').Value[1].Value
$LonMIN = $image.Properties.Item('GpsLongitude').Value[2].Value
$LonSEC = $image.Properties.Item('GpsLongitude').Value[3].Value
$LonREF = $image.Properties.Item('GpsLongitudeRef').Value
# Convert them to Degrees Minutes Seconds Ref
$LatSTR = "$LatDEG$([char]176) $LatMIN$([char]39) $LatSEC$([char]34) $LatREF"
$LonSTR = "$LonDEG$([char]176) $LonMIN$([char]39) $LonSEC$([char]34) $LonREF"
# Write the full coordinates out
Write-Host "$LatSTR $LonSTR"
}
catch
{
Write-Host "NONE"
}
}
Obviously you can remove the Write-Host "$FileName"
(file path and name) if you just want a list of coordinates. Or you can change it to Write-Host "$_"
if you only want the file name.
You can add -Recurse
to Get-ChildItem $FolderPath -Filter *.* |
so it would be Get-ChildItem $FolderPath -Filter *.* -Recurse |
to look at all files in all sub-folders.
Solution 4:[4]
@dwids - to show progress, you can use the 'progress' feature of exiftool as documented in the exiftool 'pod' https://exiftool.org/exiftool_pod.html#Advanced-formatting-feature
-progress[:[TITLE]] Show the progress when processing files. Without a colon, the -progress option adds a progress count in brackets after the name of each processed file, giving the current file number and the total number of files to be processed. Implies the -v0 option, causing the names of processed files to also be printed when writing. When combined with the -if option, the total count includes all files before the condition is applied, but files that fail the condition will not have their names printed.
If followed by a colon (ie. -progress:), the console window title is set according to the specified TITLE string. If no TITLE is given, a default TITLE string of "ExifTool %p%%" is assumed. In the string, %f represents the file name, %p is the progress as a percent, %r is the progress as a ratio, %##b is a progress bar of width "##" (20 characters if "##" is omitted), and %% is a % character. May be combined with the normal -progress option to also show the progress count in console messages. (Note: For this feature to function correctly on Mac/Linux, stderr must go to the console.)
Rather than rework your example, the code below is an extract from working code:
$sourcedir = 'fewphotos'
$exifargs = 'exiftool -json -d %Y%m%dT%H%M%S%z -Model -DateTimeOriginal -ext jpg -progress:%50b -r ' + $sourcedir
$exifdata = invoke-expression $exifargs | ConvertFrom-Json
The progress bar will appear in the frame of the window in which exiftool is running. %50b will create a new marker for every 2% of files processed by exiftool.
The example also shows how the exiftool command can include variables, e.g. for the source directory. The example works under both Linux and Windows.
Hope this helps.
Should be a comment but I don't have enough rep.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | stackprotector |
Solution 2 | |
Solution 3 | |
Solution 4 | user8150417 |