Skip to content
项目

Oldcircle/geo-sleuth has 6,555 GitHub stars and 803 forks. GitHot measured +121 stars in 24 hours and +940 in 7 days. Last checked 2026-11-09 02:03 UTC. Last push 2026-11-03.

这些数字如何统计

一项编程智能体技能,可通过 OpenStreetMap 几何数据、高程天际线、卫星影像和街景查找照片的拍摄地点,并展示工作过程。支持 Claude Code、Codex、Cursor、Gemini CLI、OpenCode 和 GitHub Copilot。

6,555 星标803 forks +121 24小时

实测星标增长

24小时
+121
7天
+940
15天
正在收集历史
1个月
正在收集历史

各周期内的星标净变化,周期相互重叠,并非每日新增。缺失的测量保持空白。星标代表关注度,不代表软件质量。

README.zh-CN.md

原文

🧭 geo-sleuth

一个 agent skill,能找到照片的拍摄地点——并展示它的推理过程。

English Simplified Chinese Japanese

License: MIT Python 3.10+ Agent Skill: SKILL.md PRs welcome GitHub stars

适用于
Claude Code Codex Cursor Gemini CLI OpenCode GitHub Copilot
……以及其他任何能读取 SKILL.md 并运行 shell 命令的 agent。

From one photo to a camera position: the photo, the region scan, the skyline overlays, the evidence image

没有文字,没有车牌,没有地标。一座桥,一座山。定位误差在 2 米以内。


快速开始

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
17 piers marked on the viaduct, spacing used as a ruler
桥墩当尺子:左边间距宽表示近,右边间距密表示远。

Top three skyline overlays: Fuzhou, Qingyuan, Huizhou Evidence image: camera position, field of view, the railway and the mountain
左:前三名的山脊线画回照片。右:skill 生成的证据图。
本次实战的更多图
Region scan: railway bridges in grey, candidate sites in orange
区域扫描:区域内所有铁路桥(灰色),通过地平线检验的候选点(橙色)。

Bearings to 17 piers intersecting the railway line
数桥墩: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 描出照片里的山脊,terrain.py fit 和每处看到的山脊逐一比对。青海前 25 处都在 9.6–11.7 px 之间,真值排第 9。 卡住
歪斜 手机歪 1°,2048 px 宽的画面边缘山脊偏 1024 × tan 1° ≈ 18 px,平均约 10 px,正好是候选之间的差距。在真值处,误差沿画面横向落在一条直线上,斜率是 tan θ,θ ≈ 1°。于是 fit 改成和地平线偏移一起解横滚。
重新拟合 解了横滚之后,真值从 11.1 px 降到 6.2 px,第二名是 9.4 px。 1 处
卫星图 视线方向 1 km 内有三座新月形沙丘和一片帐篷营地,东边 1–2 km 是 750/330/110 kV 线束,所以电塔只出现在照片左半边。机位在营地西边那座沙丘的脊上,朝 205°。 一道沙脊
青海、甘肃、宁夏的高压线,橙点是候选处,红星是机位
OSM 高压线(金色)、山能占满视野的候选处(橙色)和机位(红星)。新疆的候选处没有画。

真值处照片山脊与算出的山脊,解横滚前后,以及误差沿画面的分布
真值处解横滚前后。上:误差从画面一侧到另一侧逐渐变大,斜率给出角度。下:解了横滚,这个趋势没有了。

假设手机水平时青海 408 处的天际线误差,以及解横滚后前 12 处的误差
每处的天际线误差。假设手机水平时,真值混在一堆候选里;解了横滚,只有它低于 9 px。

证据图:沙脊上的机位、视野、沙丘、营地和高压线束
证据图。OSM 高压线(金色)和卫星图上的电塔对得上。

盲测复跑。 后来我们把照片交给一个新开的 agent,用现在的 skill,只给一句提示:在中国。它扫了青海 5,334 条高压线,拟合 792 处,选出的一处误差 0.224°,第二名 0.357°。最后落在同一道沙脊上,距确认的机位 87 m,用时 32 分钟。为这张照片加的横滚拟合现在已经是 terrain.py fit 的一部分;为什么用这个案例做核心改动的范本,见参与贡献。

复现条件与检查点
  • 两次运行:上表是最初解出这张照片、并因此加入横滚拟合的过程(Claude Opus 5.0);"盲测复跑"是后来用 Claude Opus 5.5 新开一个 agent 跑的。
  • 盲测的输入和提示:原图 2048×1152;提示只有一句"在中国"。README 里的图是展示用的版本。
  • 盲测检查点:青海高压线 5,334 条;terrain.py scan 用默认的 --cluster-km 3.5,14,843 个命中聚成 792 簇;792 簇全部送进 terrain.py fit(分 6 批),山脊拟合之前没有按卫星图排名截断;最好的一处 0.224°,第二 0.357°;再用卫星图在视线方向上找沙丘和营地。共 87 次工具调用,32 分钟。
  • 聚类与细化的衔接:fit 只围着每簇的代表点布机位。把 --cluster-km 调大后,簇里离代表点远的命中可能根本没被拟合;现在 scan 会记下每簇的范围(extent_m),簇比 fit --radius 宽时 fit 会报 WARNING。
  • 示例:examples/desert-roll-fix/ 用固定的 12 处复现横滚拟合前后(真值从第 8 名、12.0 px 到第 1 名、6.2 px)。

3. 温哥华街头的一棵樱花树

人行道上方一棵盛开的樱花。车牌和街道设施说明是温哥华,难的是找到哪条街:温哥华有将近一万段街。

照片 → 温哥华 → 1,095 棵大樱花 → 97 段街 → 72 段测横坡 → 西 60 街 → 3 m

步骤 它做了什么 剩余
读图 BC 省白底蓝字车牌、草坪隔离带里的墨绿色路灯杆、Vancouver Special 式住宅。 温哥华
行道树数据 温哥华公开了每一棵行道树的树种、胸径和门牌。候选从数据里来,没有去挑有名的樱花街:先取胸径 ≥ 40 cm 的观赏樱,再取至少有三棵的街段。 1,095 棵,97 段
影子和坡 车影落向左前方、朝着镜头,太阳在前方偏右。镜头这一侧的院子高出人行道、砌着石墙,这一侧是坡上。agent 用高程数据测了 72 段街的横坡,再对数据里大树长在街的哪一侧。 几段
一棵棵对 西 60 街 100 号段,北侧是一排大樱花;南侧往前 30–60 m 都是 7.6 cm 的小树,下一棵大的在约 75 m 外。照片里也是这样:右侧近处没有高树,远处是粉色树冠。 1 段
街景 2009 年和 2024 年两期街景都能看到砌石挡土墙和石阶、绿色路灯杆和白房子。带屋顶露台的那栋房子在 2024 年那期和照片里都有。机位在北侧人行道,朝东。 3 m
温哥华的行道树、大樱花、候选街段和答案 照片里的线索:路灯、车牌、车影、高出的院子、屋顶露台
左:市政数据里有坐标的 184,517 棵行道树、1,095 棵大樱花和 97 段候选街。右:照片透露了什么。

照片与同一段街 2024 年、2009 年的街景
照片是在人行道上拍的,街景车走在路中间,所以角度不同。

证据图:西 60 街 100 号段,机位在北侧人行道朝东,行道树来自市政数据
证据图。粉圈是市政数据里的大樱花,绿圈是小树。

盲测复跑。 上表就是盲测的过程:新开一个 agent,用现在的 skill,只给照片,不给提示。53 分钟后给出的机位距确认位置 3 m。

复现条件与检查点
  • 这是什么记录:上表就是盲测复跑(Claude Opus 5.5,新开一个 agent,只给照片,不给提示),共 154 次工具调用,53 分钟。
  • 输入:原图 1440×1920。README 里的图先裁切再缩小,像素约为原图的 1/16,不适合当输入。
  • 检查点:市政数据里有坐标的行道树 184,517 棵 → 胸径 ≥ 40 cm 的观赏樱 1,095 棵 → 至少有三棵的街段 97 段 → 能测横坡的 72 段 → 按大树在街的哪一侧、坡向留下几段。
  • 街景核验:match.py 整图排序不理想(盛花期,照片在人行道上拍而街景车走在路中间),于是用 --query-box 0,900,1440,1600 只比树所在的区域,再取 2009 / 2024 两期街景核对石墙、台阶、路灯杆这些不变的地物。真值处 SIFT 只有 0–5 个内点,没有据此排除。match.py 输出里 inliers 为空(refined: false)表示没做细排,不是 0 个匹配。

安装

geo-sleuth 是一个标准的 Agent Skill:一个文件夹,里面是 SKILL.md、scripts/、references/ 和 data/。可以用 skills 命令行工具安装,也可以自己复制文件夹。

一条命令,给全部六个 agent 装到用户级目录:

npx skills add Oldcircle/geo-sleuth -g -a claude-code -a codex -a cursor -a gemini-cli -a opencode -a github-copilot -y

手动安装:

git clone https://github.com/Oldcircle/geo-sleuth
mkdir -p ~/.agents/skills ~/.claude/skills
cp -r geo-sleuth/skills/geo-sleuth ~/.agents/skills/              # Codex, Cursor, Gemini CLI, OpenCode, GitHub Copilot
ln -s ~/.agents/skills/geo-sleuth ~/.claude/skills/geo-sleuth     # Claude Code

Codex、Cursor、Gemini CLI、OpenCode 和 GitHub Copilot 都会读取 ~/.agents/skills/,所以在这里放一份就能同时覆盖这五个。各 agent 自己的目录如下,均摘自其官方文档:

Agent 用户级 项目级
Claude Code ~/.claude/skills/ .claude/skills/
Codex ~/.agents/skills/ .agents/skills/
Cursor ~/.cursor/skills/ 或 ~/.agents/skills/ .cursor/skills/ 或 .agents/skills/
Gemini CLI ~/.gemini/skills/ 或 ~/.agents/skills/ .gemini/skills/ 或 .agents/skills/
OpenCode ~/.config/opencode/skills/ 或 ~/.agents/skills/ .opencode/skills/ 或 .agents/skills/
GitHub Copilot ~/.copilot/skills/ 或 ~/.agents/skills/ .github/skills/ 或 .agents/skills/

其他任何能读取 SKILL.md 并运行 shell 命令的 agent 用法都一样:把文件夹放到它查找 skill 的位置即可。

它怎么工作

整个工作分成三层:脚本负责决策,脚本负责感知和排序,模型只在排名靠前的少数几个里做判断。

flowchart LR
  A["photo"] --> B["intake.py<br/>EXIF · OCR · reverse image search"]
  B --> C["board.py<br/>candidate board: clues, likelihood ratios, ranking, next step"]
  C --> D{"which branch?"}
  D --> E["sun.py · terrain.py · osm.py · pose.py<br/>shadows, skylines, OSM corridors, camera pose"]
  D --> F["sat_scan.py · match.py · gsv.py · baidu_pano.py<br/>CLIP-ranked satellite tiles, DINOv2+SIFT street view"]
  E --> G["board.py check · report"]
  F --> G
  G --> H["evidence.py<br/>coordinates ± radius · evidence image · graded confidence"]
层 谁做 工具
决策:候选有哪些、证据怎么打分、什么能排除、下一步扫哪里 脚本(候选盘) board.py
感知:读文字、查表、在卫星图里找目标、比对街景 脚本先排序,人只看排名靠前的几个 intake.py ocr.py clues.py sat_scan.py match.py geo.py
判断:从画面里提取线索、提出假设、在排好的少数候选里挑选 模型 SKILL.md + references/

每个结论都必须对应本次会话里真正跑过的命令、以及它产出的文件。排除需要读到或算出的证据;观察和猜测只能降低候选的权重。

工具箱

二十多个脚本,一个脚本干一件事。完整表格(含数据来源)在 skills/geo-sleuth/references/data-sources.md。

能做什么 脚本
EXIF:GPS、拍摄时间、等效焦距、朝向 exif.py
整图、放大裁图和切块的 OCR(macOS 上用 Apple Vision,其他系统用 RapidOCR) ocr.py
百度和 Yandex 以图搜图,相似图片拼成带编号的图片墙;关键词搜图 revimg.py
一条命令完成第 0–3 步:元数据、边缘裁图、变体、OCR、以图搜图 → 生成 intake.md intake.py
放大裁图、边缘和角落裁图、切块、提取桥墩等等间距结构的像素列 imgprep.py
查表:全球的国际电话区号、靠左/靠右行驶、海外属地;地区包里的车牌前缀、固话区号、行政区划 clues.py + data/ + regions/
地区包:列出、打印某国的卡片和线索索引、检查格式 regions.py
候选盘:候选、线索、似然比、排除、排名、扫描顺序、出结论前检查 board.py
地名录:列出下级行政区及其边界框、建成区范围 gazetteer.py
地名、小区名或店名 → 坐标候选,同名点全部列出 poi.py
太阳与影子:纬度带、时刻、街道走向、受光面推朝向、真方位角 sun.py
OSM Overpass:要素共现、线转点、路线走廊、线线相交、街道格局模板 osm.py
卫星图拼接、标点、带编号的缩略图 tiles.py
对卫星图网格或候选点做 CLIP 零样本打分(跑道、厂房、筒仓、水坝……) sat_scan.py
百度全景 / Google 街景:找点、渲染朝向、缩略图、历史批次 baidu_pano.py gsv.py
把候选实景图与照片排名比对:DINOv2 全局相似度 + SIFT 内点 match.py
高程:合成山体视图、天际线叠图、剖面;线状要素 × 地形扫描、山脊提取、天际线批量打分 terrain.py
多点反解机位:经纬度、高度、朝向、俯仰、横滚,附误差半径 pose.py
方位角、距离、视线相交、对齐线、画框/遮挡检查、由等间距结构反解机位 geo.py
证据图:卫星图 + 机位扇形 + 对比网格 evidence.py

案例 1 的几步(区域扫描、天际线批量打分、由桥墩间距反解机位)都已内置在 skill 里,对应子命令:terrain.py scan / ridge / fit、imgprep.py piers、geo.py spacing;按那张照片调好参数的案例脚本放在 examples/rail-skyline-session/。案例 2 的歪斜由 terrain.py fit 自己解(--roll-max,默认 1°),examples/desert-roll-fix/ 可以复现修改前后。

基准测试

按算子分别测量:

脚本 测试 结果
match.py 8 个案例:把百度全景历史批次渲染成照片,150 米内的全景作为候选(深圳) 真值排名为 1/2/4/1/1 和 5/1/6,全部进入前 6,一半排第 1
sat_scan.py 4×8 公里,z17 级 364 格,40 条 OSM 标注的跑道作为真值,多尺度(深圳) recall@20 为 17/40,@30 为 22/40,@100 为 32/40,中位排名 23
terrain.py scan / fit + geo.py spacing 在上面案例照片上做限定范围复跑 真值所在的候选簇排第 1,最终位置离真值约 2 m
terrain.py fit --roll-max 8 道合成天际线题(随机朝向、焦距、±1° 横滚) 位置误差中位 324 → 238 m(不歪的题 324 → 89 m,歪的 1,695 → 265 m)
terrain.py fit --roll-max 沙漠案例的 12 个最佳候选处 真值从第 8 名、12.0 px → 第 1 名、6.2 px(第 2 名 9.9 px)
clues.py 6 张表,抽查 9 个值 9/9 正确

端到端:新开一个 agent,只给照片(和表里的提示)盲测复跑:

案例 提示 结果 用时
沙漠天际线 「在中国」 距确认的机位 87 m 32 分钟
樱花街 无 距确认的机位 3 m 53 分钟

这两张照片以前都解过,从中学到的东西已经写进 skill,所以这是回归检查,不代表没见过的照片上的准确率。

这套方法来自拆解网络定位博主的 14 个视频、22 道题和一批实战记录,再把其中管用的做法写成规则和脚本。v2 把所有能写成代码的规则都放进 board.py,让规则真正被执行,而不只是被读一遍。

环境要求

需要 Python 3.10+、uv、curl,以及能运行 shell 命令的 agent。每个脚本声明自己的依赖,用 uv run 首次运行时安装;安装时保留完整 skill 文件夹,包括 data/ 和 scripts/ 中的辅助模块。

以图搜图需要 Google Chrome 或 Playwright Chromium,先尝试 Chrome,启动失败则回退到 Chromium。两者都没有时运行 uvx playwright install chromium。Linux 缺少系统库时可用 uvx playwright install --with-deps chromium;Playwright 升级后若提示缺少浏览器可执行文件,重新运行安装命令。

match.py 和 sat_scan.py 会优先使用当前 torch 可用的 CUDA,其次是 MPS,最后是 CPU,并打印所选设备(如 model ... ready (cuda, ...))。设置环境变量 GEO_DEVICE=cpu|cuda|mps 可指定设备。Windows 上默认 uv run 安装的是仅支持 CPU 的 torch;使用 NVIDIA GPU 时,添加驱动支持的 CUDA 构建(nvidia-smi 显示驱动支持的最高 CUDA 版本,cu126 代表 CUDA 12.6):

uv run --find-links https://download.pytorch.org/whl/cu126/torch/ skills/geo-sleuth/scripts/match.py rank --query photo.jpg --images cands/ --out ranked.json

这个选项只添加 torch 构建,其他包仍从 PyPI 安装。首次运行需下载约 2.4 GB。

在仓库根目录自检(PowerShell 也可运行):

uv run skills/geo-sleuth/scripts/doctor.py
uv run skills/geo-sleuth/scripts/doctor.py --network

已安装 skill 时换成其实际的 scripts/doctor.py 路径,或让 agent 运行。自检会检查 Python、uv、curl、查表数据、当前目录写权限,以及浏览器能否实际启动。--network 额外探测各服务,不上传照片;--json 输出诊断数据。退出码 1 表示有失败项,警告表示部分可选功能受影响。首次运行 uv 可能需要安装 Playwright;自检不加载 OCR 或 ML 模型。网站可达不等于识图上传、模型下载和街景查询一定成功。

先用自己的照片检查本地处理流程:

uv run skills/geo-sleuth/scripts/intake.py photo.jpg --out-dir intake --no-rev

打开 intake/intake.md,检查裁图和 OCR 结果;去掉 --no-rev 才会在线以图搜图。macOS 优先 Apple Vision,其他系统或回退场景使用 RapidOCR。match.py、sat_scan.py 首次运行会安装 ML 依赖并下载模型,需预留时间和磁盘空间;首次安装依赖仍需联网。

排错:浏览器启动失败就安装 Chrome / Chromium;HTTP 403/429 或验证码先查看截图、稍后重试;连接超时先跑 doctor.py --network。模型加载失败要看磁盘空间、下载错误和 Hugging Face 连通性。intake.md 会区分跳过、失败和已完成的步骤;搜索失败不能当作“没有匹配图片”。

语言:维护中的指令、CLI 帮助、报错、报告标题和默认证据标注均为英文。照片 OCR、地名、第三方响应和截图保留原文,agent 负责解释,并用用户的语言写最终报告。中文指令的最后一版冻结在 tag zh-final,不再更新;要装那一版:npx skills add https://github.com/Oldcircle/geo-sleuth/tree/zh-final/skills/geo-sleuth。

路线图

  • 针对 terrain.py scan / fit 做合成地形案例的算子级测试
  • 把 Google Lens 接入作为第三个识图引擎
  • 在 Linux 和 Windows 上跑 CI
  • 用一批没见过的照片建一套公开的盲测集,给出端到端准确率

参与贡献

欢迎提 issue 和 pull request,参见 CONTRIBUTING.md。

你有的是 放在哪 门槛
能区分国家的线索 references/clues/global.md 有来源、有反例
只对一个国家有用的线索、表或服务 地区包 regions/<cc>/(契约) 只放数据和文档;regions.py lint 输出 ok
对核心脚本的改动 单独一个 PR 一张改前解不出、改后解出的照片,外加别处没有退步
一次跑错的记录 issue 第一个走偏的步骤

沙漠案例是核心改动的范本:skill 在一张真实照片上卡住(前 25 处挤在 9.6–11.7 px);改动是通用的,不是给这张照片调参(在 terrain.py fit 里解相机横滚,上限 1°,--roll-max 0 给出旧结果);同一张照片随后解出(真值跳到 6.2 px);在不是为它设计的题上也站得住(8 道合成天际线、一道更早的实拍题)。examples/desert-roll-fix/ 用两条命令复现修改前后。

Star 历史

致谢

  • OpenStreetMap 贡献者(ODbL)。本仓库不附带 OSM 数据,脚本都是实时查询;发布查询结果时请注明 © OpenStreetMap contributors。
  • AWS Terrain Tiles(Terrarium 格式高程数据)。
  • modood/Administrative-divisions-of-China。
  • DINOv2(Meta AI)、CLIP(OpenAI)。
  • 查表数据的来源和许可证见 skills/geo-sleuth/data/README.md。

许可证

MIT,见 LICENSE。data/ 目录中源自维基百科的表格采用 CC BY-SA 4.0 许可,见 skills/geo-sleuth/data/README.md。

负责任地使用: 只用在你自己的照片、或你已获准分析的照片上,绝不要用它去找没有同意被找到的人。

探索相关项目