Eric Yap · Entries

photo-geo

Puts a position on photos that have none, using the geotagged photos around them.

Question

What were my plane-window photos pointing at?

A Mac Photos library of 26,308 photos held 4,054 with a capture time but no location. Photos labels some of them “Porthole”. Nothing in the library said where the aircraft was when each one was taken. Built in one afternoon, by an operator with no GIS background, to answer that one question.

Built

A small Python tool that infers a position for each untagged photo from the geotagged photos taken around it, then writes the fix into a copy of the file. The library itself is never modified.

Methods, in the order tried:

methodwhenconfidence
interpolategeotagged anchors on both sides, nearest within 15 min; spherical interpolationhigh
dead_reckonone anchor within 15 min; speed and heading derived from that anchor and its same-side neighbourmedium
nearest_anchorone anchor within 15 min, no usable velocitymedium
flight_modelno anchor within 15 min, same-day anchors more than 300 km apart; great circle, back-calculated from the arrival anchor at 850 km/h with a 20-minute landing allowancelow
interpolate_widesame-day anchors both sides, beyond 15 min, short separationlow
nearest_anchor_dayone same-day anchor onlyvery low

An implied ground speed above 1,000 km/h between anchors demotes the fix to low: one of the anchors is lying.

What it writes, via exiftool, into a copy of each camera-captured file: GPSLatitude and GPSLongitude; GPSProcessingMethod = INFERRED:<method>, visible in any EXIF viewer; and an XMP namespace carrying the method, confidence, the anchors used and the time of inference. Provenance travels with every fix, so an estimate can never be mistaken for a measurement.

Runs on macOS with the Photos library local: osxphotos for the query, Photos’ own AppleScript export for the originals, exiftool for the write. The outputs are a KML and a GeoJSON with thumbnails.

Found

1. Photos stamps untagged shots with a guessed time zone, and the guess contradicts the GPS on the same flight

Photos stores every capture with a time-zone offset. For a geotagged photo the offset comes from the GPS fix. For an untagged photo it is a guess. On the 2016-02-01 Bandung→Kuala Lumpur leg the guess was +08:00 while the geotagged frames taken thirteen seconds later on the same aircraft carried +07:00. Comparing the two in UTC moves the untagged frame one hour along the flight path, roughly 850 km, and drops it in the sea off Lampung. Comparing them on the camera wall clock, offset stripped, puts it thirteen seconds before its neighbour, over the east coast of Pulau Rupat, Riau.

Rule the tool follows: compare timestamps on the camera wall clock, never in UTC, whenever one side of the comparison has no GPS. The camera clock is the one quantity both photos share.

2. A received image geotagged with the recipient’s location is a false fix, so the tool refuses to write it

Of the 4,054 untagged photos, 3,680 have no camera EXIF at all. They are WhatsApp, WeChat, LINE and Telegram receipts, screenshots and saved images. Their timestamp is the moment they arrived on the phone. Every mainstream geotagging tool will happily stamp them with wherever the phone was at that moment, and from then on the file carries a location that has nothing to do with the picture.

The tool computes those positions, useful for “where was I when this arrived”, and shows them in a hidden KML folder, but never writes GPS into a file that has no camera EXIF.

Became

A validated, public tool. IMG_3542, 2016-02-01 16:00:19, untagged, iPhone 6s, was placed at 2.050N 101.712E by interpolate at high confidence. Against Esri World Imagery of Rupat Utara’s east coast, the hairpin double meander with a rectangular clearing inside the bend, the river mouth with its sand spit and sediment plume, the shore village on the near bank, the second village across the river and the straight inland road all line up. OpenStreetMap reverse-geocodes the mouth to Kadur, Rupat Utara, Bengkalis, Riau. The aircraft was directly over the village.

IMG_3542 beside Esri imagery of Rupat Utara's east coast
Left: the frame, rotated relative to north. Right: current Esri World Imagery, 2.02–2.06N 101.69–101.74E. Match the river loop and the mouth.

What it found in one library:

photos26,308
untagged with a capture time4,054
of those, real camera captures374
placed324 (81 high, 9 medium, 137 low, 97 very low)
labelled “Porthole” by Photos26
porthole frames containing land4, one place: Kadur, Rupat Utara

Everything else through the window was wing, cloud deck and sun on a scratched pane. The twenty flight_model frames over the Pacific south of Japan remain unverified and are labelled low. That is the point of the label. Released under MIT at the repository above.

ProvesP4, AI-Powered Data Analysis for Business Decision-Making. A question, a dataset, a method chain with stated confidence, a validation against an independent source.