漫游图鉴 · 兴趣旅行攻略生成器
概述
面向建筑、艺术、美食、摄影四类兴趣(默认主题,guide.json 可用 interest_order 自定义增减)的
旅行攻略生成器。适用于任何目的地、任何人群(独行/情侣/亲子/团建)、任何出行方式(自驾/铁路/城市漫步)。
输入一份 guide.json(城市顺序、停留天数、酒店、每站按兴趣分类的打卡点、转场里程),即可一键产出:
- 无重叠标签路线图(PNG)——节点四行标注(中文/英文/行程日·日期/酒店),连线中点标注「km · 限速」
- 按站组织的攻略 Markdown——每站含 建筑/艺术/美食/摄影 分节亮点 + 酒店 + 交通 + 逐日概览
- 路线里程/限速表 + 预算表(MD 内嵌表格)
完整示例数据见 guide.example.json;运行后自动生成同名 _route_map.png 路线图(标签零重叠)。
快速开始
# 1) 用加东自驾示例跑通全流程(生成路线图 + 攻略 MD)
python3 scripts/build_trip_guide.py guide.example.json
# 2) 用京都 5 日漫步示例验证「非加东 / 城市徒步」通用场景
python3 scripts/build_trip_guide.py guide.kyoto.example.json
# 3) 换成自己的攻略: 复制任一示例改数据, 再执行
python3 scripts/build_trip_guide.py 我的攻略.json
输出(默认同目录):
- <同名>_route_map.png(路线图,运行脚本后生成)
- guide.example.md 或 <同名>.md(攻略正文)
- 终端打印 COLLISION-CHECK OK: 0 处标签重叠(自检通过标志)
guide.json 数据格式
{
"title": "加拿大金秋 20 天自驾攻略",
"subtitle": "多伦多进出 · 纯加东闭环 · 红叶最佳排期",
"people": "情侣 / 夫妻 (2 人)",
"date_range": "2026.09.19 — 10.08 (20 天)",
"canvas": [1800, 1320],
"stops": [
{
"id": 1,
"name": "多伦多", "en": "Toronto",
"days": "D1-D3", "dates": "9/19-22",
"hotel": "19 Grand Trunk Cres",
"x": 280, "y": 900,
"color": "#185FA5",
"pref": ["right", "above", "below", "left"],
"interests": {
"建筑": ["CN Tower", "老市政厅", "圣三一教堂"],
"艺术": ["AGO 安大略美术馆", "涂鸦巷 Graffiti Alley"],
"美食": ["圣劳伦斯市场", "唐人街早茶"],
"摄影": ["Harbourfront 湖滨日落", "Ripley's 夜景"]
}
}
],
"legs": [
{"from": 1, "to": 2, "km": "130km", "speed": "100", "note": "QEW/403"}
],
"budget": [
{"item": "机票+租车(已订)", "amount": "¥36,294"},
{"item": "酒店 19 晚套餐", "amount": "¥23,289"}
],
"legend": [["#BA7517", "红叶峰心"], ["#0F6E56", "枫海首盛"]],
"daily_plans": [
{"day": "D1", "date": "9/19", "city": "多伦多",
"plan": ["12:20 MU207 起飞", "14:25 落地 YYZ", "16:10 入住 19 Grand Trunk"]}
]
}
字段说明:
- stops:一站=一个城市/住宿地。x/y 为画布坐标(canvas 系,越分散越不容易重叠);
interests 四类任填,空数组则该类在攻略中省略;主题顺序由 interest_order(可选,默认 建筑/艺术/美食/摄影)决定
- legs:转场段;from/to 引用 stops 的 id;km/speed 显示为「130km · 100」
- daily_plans:可选,提供后攻略按日展开(每个城市→每日行程清单);缺省时按站输出兴趣亮点
- legend:可选,图例项(颜色+文字)
- 节点标签第 4 行(酒店)超 18 字符自动截断加 …
生成工作流(Agent 执行指南)
- 确认行程骨架:城市顺序、每城停留天数、住宿酒店/地址、转场方式(自驾/火车/航班)与里程
- 按四大兴趣归类亮点(每站给 2-5 个,宁缺毋滥):
- 建筑:地标建筑、老城区、教堂/博物馆建筑、街区肌理
- 艺术:美术馆/画廊、涂鸦墙、演出、设计街区
- 美食:市场、本地菜系、必吃单品、推荐餐厅(注明人均档位)
- 摄影:日出日落机位、夜景、红叶/观星点、出片时段 - 填坐标:
stops的x/y按真实相对方位铺开(左上=西北),画布 1800×1320 - 生成 + 校验:运行 build_trip_guide.py;自检必须输出
0 处标签重叠;
若图太挤 → 调大canvas、把 stops 坐标分散、或减小节点字号 - 转 PDF(可选):MD 内容可进一步用 reportlab/Word 排版输出 PDF
兴趣亮点写作规范
- 每类 2-5 条,短语式(如「Harbourfront 湖滨日落」),便于路线图周边引用
- 摄影类必须给机位/时间(例:「CN Tower 观景台 17:30 蓝调时刻」)
- 美食类标注品类+档次(例:「枫糖培根熏肉三明治 · 人均 $12」)
- 建筑/艺术给名称+看点一句话(例:「老市政厅 · 罗马复兴式立面」)
脚本与参数
| 文件 | 作用 |
|---|---|
scripts/build_trip_guide.py |
主入口:guide.json → 路线图 PNG + 攻略 MD |
scripts/collision_layout.py |
防重叠布局引擎(节点外推/连线多候选/全局消解),可单独 python3 collision_layout.py 生成示例图 |
scripts/verify_crop.py |
PDF/图片嵌入后像素级验证:渲染→按节点坐标裁切→拼图目检 |
guide.example.json |
完整示例数据(加拿大 8 城自驾) |
guide.kyoto.example.json |
通用性示例(京都 5 日城市徒步,验证非加东场景) |
build_trip_guide.py 支持命令行覆盖:
python3 scripts/build_trip_guide.py guide.json --md out.md --map out.png --no-md --no-map
常见问题
- 中文字体找不到:
collision_layout.py内置 macOS/Linux/Windows CJK 字体路径探测
(STHeiti/PingFang/文泉驿/微软雅黑/黑体);仍失败则把字体路径加入FONT_CANDIDATES - 图太挤有重叠警告:优先调大
canvas、把stops坐标分散、或调小font_sizes
(build_trip_guide.py --font-scale 0.85整体缩小) - PIL 坐标报错:画线/多边形需 int 坐标,引擎内部已
round();自行扩展绘制代码时
math.cos/sin结果记得取整 - 攻略太长:MD 按站聚合输出,如需逐日可提供
daily_plans;酒店/预算表自动生成
嵌入 PDF 后验证
路线图嵌入 PDF 后,用 scripts/verify_crop.py 做像素级确认(渲染 PDF 页→按节点坐标裁切→拼图目检):
cat > /tmp/nodes.json <<'EOF'
[{"nid":1,"name":"多伦多","x":280,"y":900}, {"nid":2,"name":"尼亚加拉","x":210,"y":1110}]
EOF
python3 scripts/verify_crop.py 你的攻略.pdf /tmp/nodes.json
# 输出 /tmp/node_grid_verify.png, 人工检查每格无压字
自检报告 COLLISION-CHECK OK: 0 处标签重叠 是确定性指标(所有包围盒两两不相交含安全边距),
任何缩放都不会再引入重叠;裁切图仅作最终人工确认。