向用户询问以下信息(可合并为一次提问):
web_fetch 解析)若用户提供链接,使用 web_fetch 解析笔记内容,提取:
若未提供链接,使用 web_search 搜索:
小红书 {城市} {主题} citywalk 路线 2026
小红书 {街区} 探店 打卡 攻略
提取高频出现的站点(按提及次数排序,取 Top 5-8)。
站点规划原则:
每个站点需通过 Nominatim OSM 查询获取精确坐标(见 Step 3.5),禁止凭记忆估算:
{ "name": "站点名", "lat": 纬度, "lng": 经度 }
使用脚本 scripts/verify_coords.py 校验所有站点坐标,生成地图前必须通过校验。
执行方式:
# 基础校验(生成报告)
python3 .codebuddy/skills/citywalk-route-generator/scripts/verify_coords.py \
--json stations.json --city 北京
# 自动修正 ERROR 级别坐标并输出修正后的 JSON
python3 .codebuddy/skills/citywalk-route-generator/scripts/verify_coords.py \
--json stations.json --city 上海 --auto-fix --out stations_fixed.json
校验内容:
| 校验项 | 方法 | 通过标准 |
|---|---|---|
| -------- | ------ | ---------- |
| 坐标反查 | 反查当前坐标对应的 OSM 地名 | 反查地名应包含站点名或所在街道 |
| 坐标正查 | 用站点名正向查询 OSM 推荐坐标 | 偏差 ≤150m → ✅ OK |
| 偏差距离 | 计算输入坐标与 OSM 坐标的球面距离 | 150-500m → ⚠️ WARN;>500m → ❌ ERROR |
| 路线连贯性 | 检查相邻站点距离 | <50m 可能重复;>3km 可能缺站 |
处理规则:
--auto-fix 自动替换,或手动查询 Nominatim:```
https://nominatim.openstreetmap.org/search?q={站点名} {城市}&format=json&accept-language=zh&limit=3
```
坐标获取标准流程(每个站点):
web_fetch 查询 Nominatim:```
https://nominatim.openstreetmap.org/search?q={站点名}+{城市}&format=json&limit=3&accept-language=zh
```
lat / lon 字段> ⚠️ 严禁凭记忆或 AI 知识直接填写坐标。实测偏差可达 500m-1.3km,会导致地图标记落在错误街道。
使用脚本 scripts/gen_map.py 生成地图图片并内嵌为 base64。
执行方式:
python3 .codebuddy/skills/citywalk-route-generator/scripts/gen_map.py

形式内嵌进 HTML关键技术细节(踩坑记录):
| 问题 | 错误做法 | 正确做法 |
|---|---|---|
| ------ | ---------- | ---------- |
| 地图底图黑屏 | 用腾讯/高德 JS SDK(需 Key,有域名限制) | 用 OSM 瓦片拼合或 Python 本地生成后 base64 内嵌 |
| 标记位置与底图不一致 | 静态图 + Canvas 叠加(坐标系不统一) | Python 统一坐标系直接绘制 |
| 路线太拥挤 | 固定 zoom 值 | 动态计算 zoom(ceil(min(zoomForW, zoomForH))),目标占比 80% |
| 边缘标记被截断 | canvas 加 PAD 但底图不偏移 | Python 绘制时给边缘留 40px padding |
| 站名标签遮挡路线 | 标签直接贴在标记圆上 | 标签偏移到标记上方/下方,连接小竖线 |
标题公式: {地点名}|{核心卖点}超详细打卡路线
示例:武康路咖啡漫步|全程不绕路超详细打卡路线
Hero 区副标题: 口语化,带情绪,点明核心体验
示例:梧桐树下边走边喝,5家咖啡馆+百年老建筑,随手一拍都是风景🌿
每个站点卡片 header 必须包含开放时间标签(景点/付费场所必填,街区/胡同可选):
<!-- 景点示例 -->
<span class="station-hours">🕘 09:00-17:00 | 16:30停止入场 | 周一闭馆</span>
<!-- 开放街区示例 -->
<span class="station-hours">🕘 全天开放 | 店铺约11:00营业</span>
<!-- 商业园区示例 -->
<span class="station-hours">🕘 10:00-22:00</span>
CSS 样式:
.station-hours { background:#fffbeb; color:#b7791f; padding:3px 10px; border-radius:12px; font-size:12px; font-weight:600; border:1px solid #fbd38d; }
判断规则:
每个站点卡片必须包含 4 个信息块:
📸 出片机位 — 精确到角度+时间(如"落地窗逆光,下午4点最佳")
🍽️ 必点清单 — 具体菜品/饮品名称+价格
💰 消费参考 — 分项价格区间(人均/单品)
💡 避坑提醒 — 开门时间、排队情况、注意事项等实用信息
写作规范:
以 assets/route-template.html 为基础(或直接生成完整 HTML),包含:
必需区块:
HTML 命名规范: {city}_{theme}_citywalk.html
示例:shanghai_wukang_coffee_citywalk.html
preview_url("http://localhost:PORT/xxx_citywalk.html")
检查清单:
scripts/gen_map.py)脚本核心逻辑:
# 1. 动态计算最佳 zoom
def calc_best_zoom(bounds, map_w, map_h, target_fill=0.80):
# 让路线范围占底图 target_fill 比例
# 用 ceil 而非 floor,确保路线足够大
zoom = ceil(min(zoom_for_w, zoom_for_h))
return max(10, min(19, zoom))
# 2. 坐标转换(Mercator 投影)
def to_pixel(lng, lat, center_lng, center_lat, zoom, map_w, map_h):
# 返回 [0, map_w] × [0, map_h] 范围内的像素坐标
...
# 3. 拼合 OSM 瓦片
for tx, ty in tile_range:
url = f"https://tile.openstreetmap.org/{zoom}/{tx}/{ty}.png"
# 需要设置 User-Agent,否则 OSM 拒绝请求
# 4. Pillow 绘制标注
# 绘制顺序:路线描边 → 主线 → 距离标签 → 方向箭头 → 站点圆圈 → 站名标签
# 5. 输出 base64
buf = io.BytesIO()
img.save(buf, "PNG")
b64 = base64.b64encode(buf.getvalue()).decode()
环境依赖: Pillow(标准 macOS Python 环境通常已自带)
| 信息块 | 最少条数 | 质量要求 |
|---|---|---|
| -------- | ---------- | ---------- |
| 📸 出片机位 | 3条 | 含具体角度或拍摄方向 |
| 🍽️ 必点清单 | 3条 | 含价格 |
| 💰 消费参考 | 3条 | 分项列出(单品/人均) |
| 💡 避坑提醒 | 3条 | 含时间信息或具体数字 |
| 文件 | 用途 |
|---|---|
| ------ | ------ |
scripts/gen_map.py | 本地 OSM 地图生成脚本(拼瓦片+Pillow绘制+base64输出) |
scripts/verify_coords.py | 站点坐标校验脚本(Nominatim反查+正查+路线完整性检查) |
assets/route-template.html | HTML 页面完整模板 |
examples/shanghai_wukang_rd_map.html | 上海武康路咖啡漫步完整示例 |
examples/beijing_yonghegong_citywalk.html | 北京雍和宫&国子监完整示例 |
Q: OSM 瓦片下载失败?
A: 确保网络连通,User-Agent 设为 Mozilla/5.0 CityWalkMapGen/1.0,避免被 429 拒绝。
Q: 路线标记偏离实际街道?
A: 坐标估算错误,实测偏差可达 500m-1.3km。必须先运行 verify_coords.py 校验,用 --auto-fix 自动修正 ERROR 级别坐标。
Q: verify_coords.py 报 WARN 但坐标感觉是对的?
A: WARN(150-500m)可能是 OSM 数据中该地点的中心点与实际入口位置有偏差(如大型寺庙)。人工判断后可接受,或在 JSON 中手动指定入口坐标而非中心点。
Q: 站名标签重叠?
A: 减小 zoom 或将 target_fill 调低(如 0.70),让站点间距更大。
Q: 路线图太小/太大?
A: 修改 calc_best_zoom 的 target_fill 参数(0.70-0.90)。
Q: 小红书链接无法直接访问?
A: 先用 web_fetch 访问短链,若返回重定向,再对重定向后的 URL 发起第二次 web_fetch。
Q: Nominatim 返回结果为空?
A: 尝试简化查询词(如"雍和宫"而非"雍和宫大街12号"),或加城市名限定("雍和宫 北京")。
版本: v2.0 (2026-04-13) — 基于上海武康路路线实战经验全面重写
核心原则: 地图坐标准确 > 视觉美观 > 内容丰富
共 1 个版本