0

EXIF Is Not Location Data: What Image Metadata Actually Proves

Most engineers meet EXIF through a one-liner. You pull GPSLatitude and GPSLongitude out of a JPEG, hand them to a map, and the feature is done. It works often enough that the mental model sticks: the photo knows where it was taken, and EXIF is where it keeps that knowledge.

Then you ship it, real user uploads arrive, and the GPS block is empty in the overwhelming majority of them. Worse, some of the ones that do carry coordinates are wrong in ways that are not obvious from the bytes.

This post is about what image metadata actually establishes, what destroys it, and how to write code that stays honest when the metadata is gone — which is the normal case, not the edge case.

What is actually in there

EXIF is a tag dictionary embedded in the file, mostly written by the capture device. For location work, four groups matter and they are not equally trustworthy.

The GPS IFD. GPSLatitude, GPSLongitude and GPSAltitude are stored as three rationals — degrees, minutes, seconds — plus a separate GPSLatitudeRef / GPSLongitudeRef holding N/S and E/W. Forgetting the ref tags is the classic bug: your Southern Hemisphere photos land in the Northern one, and the coordinate still looks perfectly plausible.

There is also GPSHPositioningError on some devices, and it is the most under-read tag in the whole block. A fix taken indoors or in an urban canyon can carry tens of metres of error. If you render a pin without that number, you are presenting a precision your data does not have.

Timestamps. DateTimeOriginal is local wall-clock time with no zone attached. GPSDateStamp and GPSTimeStamp are UTC. When both exist, the difference between them implies a UTC offset, which narrows the longitude band independently of the GPS block. That cross-check is cheap and it catches edited files: if the offset implies Central Europe and the coordinates say Peru, something has been rewritten.

Camera and lens identity. Make, Model, LensModel, and the maker-note blob. These say nothing about place directly, but they are strong for grouping. Photos from one device, in one session, form a set you can reason about together — and a set where one file's metadata disagrees with its neighbours is worth a second look.

Orientation and optics. Orientation, FocalLength, FocalLengthIn35mmFilm, GPSImgDirection. Focal length constrains the geometry of what you are seeing, which matters when you start comparing a photograph against street imagery. GPSImgDirection tells you where the camera was pointed — occasionally the single most useful tag in the file, and one almost nobody reads.

Absence of GPS proves nothing

The most common inference I see in code and in bug reports is that a missing GPS block means the photo was scrubbed, or is suspicious, or is old. It usually just means one of these ordinary things happened:

  • Location services were off, or the camera app never had permission.
  • The device got no fix in time — indoors, underground, in a dense city, or in the first seconds after wake.
  • The camera has no GPS radio at all. Most standalone cameras still don't.
  • The image is a screenshot. A screenshot of a photo carries the metadata of the screenshotting device, not of the original scene.
  • The image is a re-encode. Any resize, crop, filter, or format conversion by a tool that does not deliberately copy metadata will drop the whole block.
  • It went through a platform that strips metadata on upload. This is the big one.

Treat empty GPS as no information, exactly the same as never having looked. It is not evidence of tampering and it is not evidence of anything else.

The platform layer, and why your test files lie to you

Here is the operationally important part, and the reason local test fixtures mislead people: mainstream social platforms re-encode uploads and drop most metadata as a matter of course. Ingest and delivery pipelines are built to normalise images, and metadata is the first thing normalisation throws away.

The consequence for anything downloaded from a public feed is blunt: assume no GPS, no original timestamps, and a resolution far below what the camera produced. If you are building a feature around "find where this Instagram photo was taken," the metadata path is not a fallback, it is dead on arrival, and the visual-evidence path is the only path.

Note the corollary, because it cuts the other way too. Stripping happens on the platform's copy. The file sitting in the user's camera roll, the copy sent over a chat app "as a document," and the copy attached to an email are frequently untouched. Metadata leaks through the channels people forget are channels.

Two behaviours are worth verifying yourself rather than trusting any table you read online, this one included:

  1. Whether a given platform strips on upload, on delivery, or both.
  2. Whether "send as file" bypasses the re-encode in the messaging apps your users actually use.

Both change without announcement. Anything you hard-code about them will rot.

Metadata can be wrong, not just missing

A missing tag is honest. A present tag can lie, and there are mundane reasons for it.

A camera clock never set after a battery pull will happily write 2010 onto everything. A device that cached a stale fix — common when a phone wakes up and writes the last known position before the new one lands — attaches coordinates from wherever you were previously, sometimes kilometres away. Editors rewrite tags: some copy the original block verbatim into a derived file, some write their own, some write partial blocks that pass a parser and mean nothing. And anyone can set any tag deliberately; there is no signature over EXIF, so it is trivially forgeable.

The practical rule I use: metadata is a claim by the file about itself. It is a good lead and a bad conclusion. Treat it the way you would treat a filename.

Writing this in code without over-claiming

A few habits that have saved me repeatedly:

Parse defensively. Rationals with zero denominators, out-of-range values, coordinates at exactly 0,0, refs that contradict the sign, truncated IFDs, and maker notes that break naive parsers all appear in real uploads. Null Island shows up far more often than the real place does.

Keep a provenance field on every derived fact. Whether a coordinate came from the GPS IFD, from a UTC-offset inference, from a user, or from visual analysis is not a logging detail. It is the difference between two facts that look identical downstream and should never be merged.

Emit uncertainty as a first-class value. If you have GPSHPositioningError, propagate it. If you don't, don't silently substitute infinite precision. A pin on a map is a claim of precision the file usually cannot support.

Never let a stripped file silently become a confident answer. When metadata is absent, the honest response is a ranked list of candidates with the visual reasoning attached, not a single coordinate. If your API's response shape can only express one point, it will eventually express a wrong one with full confidence, because that is the only thing it can say.

Strip on your own outputs. If your service returns processed images, decide deliberately what metadata survives. Silently passing a user's home coordinates through your CDN is a privacy incident with your name on it.

When the metadata is gone

Which is most of the time. At that point the file has stopped being a container of assertions and is only a picture, and location work becomes reading what is visible: text and script, road and traffic conventions, vehicle and plate formats, utility hardware, architecture and materials, vegetation and terrain, sun angle and shadow direction.

That process is slower and it is also more defensible, because every step is something another person can check. Automated tools help mainly as candidate generators — they propose regions faster than a human can, which shortens the list you then have to disprove. What matters is whether the tool shows the clues it used. A system that returns a coordinate and no reasoning has given you nothing you can verify; a system that returns "signage script, road-marking convention, vegetation zone" has given you three things you can go and check independently.

For inspecting what a file still carries before you conclude anything about it, I maintain a browser-side EXIF viewer that parses locally and never uploads the image, which is the property you want when the file might contain someone's home address.

Useful references if you are implementing against the spec rather than against a library's guesses: the CIPA EXIF standard for tag semantics, and ExifTool's tag documentation for the maker-note reality that the spec does not cover.

The one-sentence version

EXIF tells you what a file says about itself, under conditions you cannot observe, after a processing history you usually cannot reconstruct. Read it, cross-check it, log where every fact came from — and build the part of your system that works when it isn't there, because that is the part that will run most of the time.


Disclosure: I work on an explainable photo-location tool, and the EXIF viewer linked above is ours. Everything in this post is about metadata behaviour you can verify yourself with any parser.

— Ray Lin, Singapore


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí