Install
openclaw skills install @ucsdzehualiu/geo-tag-photosRecover lost GPS metadata for JPG photos by recognizing landmarks via vision and writing GPS coordinates back into EXIF. Default dry-run; explicit --write with --backup-dir is required to modify files. Global coverage via Nominatim (OpenStreetMap), no API keys.
openclaw skills install @ucsdzehualiu/geo-tag-photosRecover GPS metadata for JPG photos. The model identifies landmarks visually, the script geocodes those landmarks via Nominatim (OpenStreetMap) and writes the coordinates back into EXIF.
This skill infers location from visual content. Do not use it to track other people, surveil private individuals, or de-anonymize photos that were intentionally stripped of location data. Inferred coordinates are best-guess estimates and must not be used for legal, forensic, evidentiary, or law-enforcement purposes.
| Can | Cannot |
|---|---|
| Identify global public landmarks (Eiffel, Opera House, Taj Mahal …) | Identify ordinary streets, residences, interiors, plain portraits, generic nature |
| Reach city- / landmark-level precision | Provide street- or building-level precision |
| Process JPG / JPEG | Process HEIC / PNG / TIFF / RAW (convert first) |
| Write GPS + ImageDescription + UserComment | Modify any other EXIF field |
| Handle a few hundred photos at a time | Reliably batch thousands (rate limits + cache hit-rate degrade) |
cd <skill-folder>
pip install -r requirements.txt
The script will refuse to run if any of piexif, Pillow, requests is
missing.
You must walk the user through these phases in order. Each phase has a clear output the user can inspect.
Identify which photos already have GPS (skip them) and which need inference.
python scripts/photo_geolocator.py scan --dir <photos> --out exif_status.csv
Read the CSV: any row with note=NO_GPS is a candidate for inference.
Anything with note=NOT_JPG must be skipped (or converted by the user first).
For every JPG with NO_GPS, use the Read tool to view the image and extract:
landmark — the landmark name (e.g. "Eiffel Tower"). If no recognizable
landmark, leave empty.city — best-guess city, or empty.country — best-guess country, or empty.confidence — high / medium / low. Be honest. low rows will be
skipped on write by default.evidence — short text: visual cues you used (architecture style, signage
language, flags, geography).Aggregate into a JSON file ai_results.json:
[
{"filename": "p1.jpg", "landmark": "Eiffel Tower", "city": "Paris",
"country": "France", "confidence": "high", "evidence": "Iron lattice tower visible"},
{"filename": "p2.jpg", "landmark": "", "city": "", "country": "",
"confidence": "low", "evidence": "interior, no landmarks"}
]
Photos with no recognizable landmark: set confidence: low and empty
strings. The pipeline will skip them on write.
python scripts/photo_geolocator.py geocode --input ai_results.json --out geocoded.csv
This calls Nominatim (rate-limited to 1 req / 1.1 s) and caches results locally. Failures are recorded in the CSV — they do not stop the run.
python scripts/photo_geolocator.py report \
--dir <photos> --geocoded geocoded.csv --out report.csv
The report shows every photo and what action will be taken: WRITE,
SKIP_HAS_GPS, SKIP_NOT_JPG, SKIP_LOW_CONFIDENCE, SKIP_NO_GEOCODE,
SKIP_AI_UNKNOWN.
Show the report path to the user. Tell them to review it. Wait for explicit confirmation before phase 5.
First, dry-run to print the planned changes (no files modified):
python scripts/photo_geolocator.py write --dir <photos> --csv report.csv
Then, after user confirmation, the real write:
python scripts/photo_geolocator.py write --dir <photos> --csv report.csv \
--write --backup-dir <photos>.bak
--backup-dir is mandatory and the script refuses if it's non-empty or
inside the source directory. Maximum 500 photos per invocation.
After writing, run scan again and compare to the report. The script also
verifies internally and reports any roundtrip mismatches.
--write without --backup-dir → exit code 2write invocation → exit code 2confidence: low rows skipped unless --include-low--overwrite-existing (with
warning)The script sends only text (landmark, city, country) to Nominatim.
Photos never leave your machine via the script. The AI vision step
happens inside your Claude session; whether the photos leave the machine for
that step is governed by your Claude setup, not by this skill.
python scripts/photo_geolocator.py cache --clearexif_io.EXIFError: not a JPG. Convert your HEIC / PNG / TIFF / RAW to
JPG first (e.g. magick mogrify -format jpg *.heic).