Oldcircle /geo-sleuth
An agent skill that finds where a photo was taken — OpenStreetMap geometry, elevation skylines, satellite imagery and street view — and shows its work. Works with Claude Code, Codex, Cursor, Gemini CLI, OpenCode and GitHub Copilot.
★ 6,775⑂ 840👁 15⑃ 46● 26▤ 0◈ 0
6,775
stars
+136
840
forks
15
watchers
46
prs
26
issues
0
releases
0
discussions
README
🧭 geo-sleuth 一个 agent skill,能找到照片的拍摄地点——并展示它的推理过程。 适用于 ……以及其他任何能读取 SKILL.md 并运行 shell 命令的 agent。 没有文字,没有车牌,没有地标。一座桥,一座山。定位误差在 2 米以内。 --- 快速开始 bash npx skills add Oldcircle/geo-sleuth 按提示勾选你用的 agent。然后把一张照片交给你的 agent,说: 找出这张照片是在哪拍的 这就是全部的交互方式。你的 agent 会读取 SKILL.md、运行脚本,然后给出机位、镜头朝向和一张卫星证据图。想自己复制文件夹?见安装。 为什么是 geo-sleuth - 一张照片,一句话。 把照片交给你的 agent,说一句这张照片是在哪拍的。你会得到机位、镜头朝向和一张卫星证据图。 - 没有文字线索也能查。 没有招牌、没有车牌、没有地标:OpenStreetMap 的几何数据、高程数据、卫星图和街景自己就能把搜索推进下去。 - 靠几何,不靠猜。 桥墩间距变成距离尺,阴影变成方位角,山脊线变成能和高程数据比对的指纹。 - 每个说法都指向一个文件。 结论必须说出本次会话里跑过的命令、以及它产出的文件。人口和名气都不算证据。 - 脚本排序,模型判断。 二十个各司一职的脚本负责搜索、打分和排序;模型只在排名靠前的几个里挑。 - 一个 skill,所有 agent 通用。 这是一个标准的 Agent Skill——SKILL.md 加上普通的 Python 脚本——所以同一个文件夹在 Claude Code、Codex、Cursor、Gemini CLI、OpenCode 和 GitHub Copilot 里都能运行。 - 答案自带误差半径。 坐标 ± 半径、镜头朝向、一张证据图和分档的置信度。 案例 这几张照片都没有 GPS 信息。每个案例写了 skill 从照片里读到什么、怎么一步步缩小范围、结果离确认的机位有多远。 这些是某个模型某一次运行的记录。skill 告诉 agent 怎么做,每一步查什么、怎么筛、看前几名由模型自己决定,换模型或换决策不保证走到同一个结果。每个案例下面的「复现条件与检查点」列出了输入、提示、关键参数和各阶段应该覆盖到什么,可以用来逐段对照自己的运行在哪一步分开。 1 · 稻田与高架铁路桥 × 天际线 × 数桥墩。 2 · 沙漠天际线高压线 × 天际线 × 手机歪了 1°。 3 · 樱花街行道树开放数据 × 影子 × 街景。 ±2 m广东清远 100 m 以内青海大柴旦 3 m加拿大温哥华 1. 稻田与高架 一张手机照片,EXIF 信息已被抹除:收割完的稻田边一个白色烤炉,远处一条长长的高架,右边一座陡峭的山。画面里没有一个字。给装了这个 skill 的 agent 发一条消息,它就给出了机位和镜头朝向。 照片 → 27,335 → 171 → 14,372 → 22 → 3 → 1 → ±2 m | 步骤 | 它做了什么 | 剩余候选 | |---|---|---| | 读图 | 高架上的杆子是接触网支柱,说明是电气化铁路。把桥墩间距当尺子用(跨距 32 米,假设):左段距拍摄点约 0.5 公里,右段超过 1 公里。陡峭的山约 3 公里外。稻子已经收割,但草还是绿的,说明还没下霜。 | 华南,是判断,不是证明 | | 区域扫描 | 从 OpenStreetMap 取出该区域内所有铁路桥:27,335 段。每 400 米采样一个点,用高程数据计算每个点 360° 的地平线。保留附近地势平坦、几公里内有明显山峰、且旁边地平线平坦的点。 | 171 处 | | 天际线拟合 | 在每个候选点周围布置候选机位,渲染每个机位看到的山脊线:14,372 个机位。前 20 名彼此相差不到 0.1°,于是加了一条约束:桥必须左近右远。 | 22 | | 叠图检查 | 把前三名的山脊线画回照片核对。第一名(福州)有一处凸起正好藏在烤炉后面,所以得分高;第三名(惠州)在照片平坦的地方却有坡度;第二名(清远)从山脚到画面边缘都贴合。 | 1 | | 数桥墩 | 照片里的 17 根桥墩变成从机位出发的 17 条方位线。它们打在铁路线上的交点必须间距均匀。结合天际线结果:先收窄到约 300 米长的一段,再收窄到一个点。 | ±2 m | 桥墩当尺子:左边间距宽表示近,右边间距密表示远。 左:前三名的山脊线画回照片。右:skill 生成的证据图。 本次实战的更多图 区域扫描:区域内所有铁路桥(灰色),通过地平线检验的候选点(橙色)。 数桥墩:17 根桥墩的方位线与铁路线相交;只有一个机位能让间距均匀。 整次运行耗时约 72 分钟,其中大约一半时间在等计算。 复现条件与检查点 - 这是什么记录:上表是这套方法第一次解出这张照片的过程(Claude Opus 5.0),不是盲测复跑;其中的步骤后来写进了 skill。 - 输入:手机照片 1280×1707,EXIF 已抹除。README 里的图是展示用的版本,不适合当输入。 - 候选生成:区域内 OSM 的全部铁路桥 27,335 段,只排除标了 electrified=no 的(当时的 rail_mtn_scan.py 这样做,现在 terrain.py scan 默认也一样),没有按 highspeed 筛。接触网只能说明是电气化铁路;正确的线路是广清城际,它在 OSM 里的 55 段桥都没有 highspeed=yes。 - 检查点:通过地平线检验的 171 处里应包含答案附近的广清城际桥段;天际线拟合 14,372 个机位,前 20 名相差不到 0.1°,靠"桥左近右远"筛到 22;叠图看前三名;最后用 17 根桥墩的方位线定点。 - 案例脚本:examples/rail-skyline-session/ 是当时按这张照片调好参数的脚本。OSM 会把一条线拆成很多段同名的 way,joint.py 会把候选中心附近的同名线路逐条试一遍,结果不受查询返回顺序影响。 2. 沙漠天际线差了一度 前景是一道沙丘,后面一排深色裸山,山脚有几座电塔。画面里没有字、路和房子,以图搜图只搜到一些泛泛的西北沙漠照片。 照片 → 1,537 处 → 卡在 10 px 上下 → 解出 1° 歪斜 → 1 处 → 一道沙脊 | 步骤 | 它做了什么 | 剩余 | |---|---|---| | 读图 | 电塔是画面里唯一的人造物,所以机位离某条高压线不过几公里。沙、戈壁和裸岩指向中国西北。 | 4 省 | | 高压线 × 地形 | osm.py geom 拉出新疆、甘肃、宁夏、青海的全部高压线,terrain.py scan 沿线用高程数据算,留下山能占满视野的地方。 | 1,537 处 | | 天际线拟合 | terrain.py ridge 描出照片里的山脊,t
Top issues
#32[run] 酒店窗景细定位:楼层首猜偏高,房号候选遗漏真实答案32252 comments · 2026-10-15#23[regions] Asia: region packs for the remaining countries2 comments · 2026-10-13#26[regions] Oceania: region packs for the remaining countries1 comments · 2026-10-13#72[bug]0 comments · 2026-11-05#69城市错误0 comments · 2026-11-03#25[regions] Americas: region packs for the remaining countries0 comments · 2026-10-13#24[regions] Africa: region packs for the remaining countries0 comments · 2026-10-13#22[regions] Europe: region packs for the remaining countries0 comments · 2026-10-13