Eric Yap · Entries
photo-geo
Repository: github.com/Technicityland/photo-geo (MIT)
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:
| method | when | confidence |
|---|---|---|
interpolate | geotagged anchors on both sides, nearest within 15 min; spherical interpolation | high |
dead_reckon | one anchor within 15 min; speed and heading derived from that anchor and its same-side neighbour | medium |
nearest_anchor | one anchor within 15 min, no usable velocity | medium |
flight_model | no 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 allowance | low |
interpolate_wide | same-day anchors both sides, beyond 15 min, short separation | low |
nearest_anchor_day | one same-day anchor only | very 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.
What it found in one library:
| photos | 26,308 |
| untagged with a capture time | 4,054 |
| of those, real camera captures | 374 |
| placed | 324 (81 high, 9 medium, 137 low, 97 very low) |
| labelled “Porthole” by Photos | 26 |
| porthole frames containing land | 4, 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.