Location, location, location
October 30, 2018 at 11:25 PM by Dr. Drang
After my two posts on extracting photo metadata using Shortcuts (the naive one and the improvement), Vinay Kashyap asked me on Twitter if I knew of a way to get the latitude and longitude of a photo. I didn’t, but with a little JSON exploration, I figured it out. And now I’ll inflict it on you.
First, you can’t use the Location property of the photo.1 Here’s a Share Sheet shortcut that tries that, where the input is restricted to Images and the contents of the Show Result step is the Location property of the Shortcut Input magic variable.
The Location property typically returns a street address, although in some of my tests, on photos taken way out in national or state parks, it returned just a city and state. But whatever it returns, it isn’t the latitude and longitude. For that we need to use the Metadata Dictionary property, which you’ll find if you scroll about two-thirds of the way down the property list.
When you choose the Metadata Dictionary, you’ll get an opportunity to choose which item of this dictionary to return. But in contrast to selecting a photo property, you don’t get a list to select from—you have to know the name (or key) of the dictionary item to return. The key we want is {GPS}
(yes, with the curly braces).
How do you know which key name to use? You don’t until you understand the dictionary structure. If we leave the key field blank, this step will return the entire dictionary in JSON format. Here’s an example, where I’ve used indentation to make the dictionary structure easier to see:
{
"{TIFF}":{
"ResolutionUnit":2,
"Software":"12.0.1",
"DateTime":"2018:10:24 09:27:26",
"XResolution":72,
"Model":"iPhone XS",
"YResolution":72,
"Make":"Apple"
},
"{Exif}":{
"DateTimeOriginal":"2018:10:24 09:27:26",
"ComponentsConfiguration":[1,2,3,0],
"ExposureBiasValue":0,
"BrightnessValue":3.152467043314501,
"SubsecTimeOriginal":"529",
"MeteringMode":5,
"FNumber":2.3999999999999999,
"FocalLength":6,
"ShutterSpeedValue":5.9093741654055014,
"SubjectArea":[785,1749,514,545],
"ApertureValue":2.5260688112781806,
"SceneType":1,
"SceneCaptureType":0,
"ColorSpace":1,
"LensSpecification":[4.25,6,1.7999999523162842,2.4000000953674316],
"PixelYDimension":2048,
"WhiteBalance":0,
"FlashPixVersion":[1,0],
"DateTimeDigitized":"2018:10:24 09:27:26",
"ISOSpeedRatings":[200],
"ExposureMode":0,
"ExifVersion":[2,2,1],
"PixelXDimension":1536,
"LensModel":"iPhone XS back dual camera 6mm f\/2.4",
"FocalLenIn35mmFilm":52,
"SensingMethod":2,
"ExposureProgram":2,
"ExposureTime":0.016666666666666666,
"SubsecTimeDigitized":"529",
"Flash":16,
"LensMake":"Apple"
},
"{GPS}":{
"ImgDirection":293.0444947209653,
"LatitudeRef":"N",
"HPositioningError":65,
"DestBearingRef":"M",
"Latitude":41.770949999999999,
"Speed":0,
"TimeStamp":"14:27:20",
"LongitudeRef":"W",
"AltitudeRef":0,
"Longitude":88.152322166666664,
"Altitude":214.22999542752629,
"DateStamp":"2018:10:24",
"DestBearing":293.0444947209653,
"ImgDirectionRef":"M",
"SpeedRef":"K"
},
"PixelHeight":2048,
"Depth":8,
"PixelWidth":1536,
"{JFIF}":{
"DensityUnit":0,
"YDensity":72,
"XDensity":72,
"JFIFVersion":[1,0,1]
},
"ProfileName":"sRGB IEC61966-2.1",
"DPIWidth":72,
"{ExifAux}":{
"Regions":{
"WidthAppliedTo":4224,
"HeightAppliedTo":3168,
"RegionList":[{
"Height":0.18228571428571427,
"Timestamp":2147483647,
"AngleInfoRoll":270,
"FaceID":2,
"AngleInfoYaw":0,
"Type":"Face",
"X":0.19619047619047617,
"Width":0.12990476190476191,
"ConfidenceLevel":889,
"Y":0.57961904761904748
}]
}
},
"DPIHeight":72,
"ColorModel":"RGB",
"{MakerApple}":{
"25":0,
"40":1,
"33":0,
"26":"q900n",
"12":[22.50390625,33.046875],
"1":10,
"35":[416,268435841],
"20":10,
"2":{
"__type__":"data",
"__value__":"PwG6AUgCmQIZAq8BAAEaABUALQCBAEEAHgAfADQAEgHPAKcAjQBxAFYAQADfAPgADwATAGgALwAcAB0AWwAgAY8AmQClAKkApgCdAJAAXwGVABUAKQAhABsAGwCIAC0BeAB4AIMAmACTAG0AYgBkAFkAHwAcABgAGAAXAEwA2gB1AHoAhQD5AIwAkwBqAGsAWABEADgAJgAYABYARwBrAHgAdwCBAMMA1ABhAH8AXABCAEUAQwA9ADIALgBKAGsAfACvANwAlABXADUAPgAvADEAXgBmAKEASABcAEkAZQD0AtMAZQBlAEMAPAAqADkASABwAGsAoQCGAJwAWABVAEgDOgDUAN4AiwDMAG8AQwBXAJUAgQAcAbUAmABZAKsApwIyAPEA4QC\/AO4AzgBSAGMAaAB2ACkBygBzAHMABwH8AkAA7gDjAI0AowBQAEsAjgCkAHsAtADSAFwATwCSANgDzwHAAKEBpQAzAEwATgChAMAAsgCmAIwAaQBVAKkAZAMtAyIDRwNnAF0B1QKPASABIADzADwBAgFrAPkAzgJmArIBpgGQAbsA4QFFAsYBDQEuAPEASQOtAmEB1AGbA2kCTwEzAVwBGgHcAW8AWAA\/AC0AkQBbAQMDBwOrArIDUwI1AV0BTwEyAYgBrwBcAEkARACDAOoA7AIDA+QCIQM="
},
"13":31,
"3":{
"flags":1,
"value":665823207958125,
"timescale":1000000000,
"epoch":0
},
"14":4,
"4":1,
"37":140,
"22":"AVxYyp2uOFH5c4umzrvZQ1fqbx7z",
"5":189,
"6":183,
"38":3,
"23":4202496,
"16":1,
"7":1,
"39":27.983028411865234,
"31":0,
"8":[-0.025775959715247154,-0.95222359895706177,-0.28761821985244751]
},
"{IPTC}":{
"DigitalCreationTime":"092726",
"DigitalCreationDate":"20181024",
"DateCreated":"20181024",
"TimeCreated":"092726"
}
}
As you can see, this is a complex nested data structure. The latitude and longitude are in a subdictionary—a dictionary within a dictionary—whose key is {GPS}
.2 The {GPS}
dictionary also includes some cool stuff like the altitude and the direction the camera was pointing, but we’ll leave them aside for now.
OK, so now that we know which item of the Metadata Dictionary to select, how do we pull the latitude and longitude out of it? The best way I found was to save the Metadata Dictionary as a variable called GPS
and then select the values of that dictionary using the key names I found in the JSON.
Putting it all together, we get this simple three-step shortcut
that returns the latitude and longitude of the photo’s location.
The simplicity of the shortcut stands in contrast to the complexity of the Metadata Dictionary and glosses over the JSON spelunking needed to figure out where the data was hiding. But now that we have one example, we can use it to pull out all kinds of metadata. I’m especially curious about the {MakerApple}
subdictionary, with its mysterious numerical key names.