关于 google-maps-api
google-maps-api 是用于调用 Google Maps Platform REST API 并获取结构化 JSON 的命令行技能,涵盖地图、路线、地点、环境和地理空间服务;需要 API 密钥并启用相应 API。
主要功能
- 01地理编码地址和坐标
- 02搜索地点并获取详情
- 03查询路线和距离矩阵
- 04查询天气、空气质量和花粉
- 05获取海拔、太阳能和道路数据
- 06生成静态地图和 Street View 图片
适用场景
- 01查找地点并查看详情
- 02规划路线并比较行程距离
- 03查询当地天气或环境状况
- 04验证地址并处理 GPS 轨迹
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ tivojn/ google-maps-api-skill --skill google-maps-api安装后在 Claude Code 中输入/google-maps-api确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
.env.example.gitignoreREADME.md.claude-plugin/plugin.jsonexamples/trip-plan-example.htmlscripts/gmaps.py详细介绍
Google Maps Platform - 通用 API 技能
关键行为规则
这些规则优先于本技能中的所有其他指令:
-
立即说明遇到的阻碍。 任何 API 调用失败时(403、REQUEST_DENIED、“未启用”等),请立即停止,并用通俗的语言告诉用户发生了什么。不要默默改用网页搜索或其他备用方案。请提出通过 Playwright 修复问题(参见下方“引导式 API 启用”部分)。
-
生成 HTML 前先询问。 未经用户同意,绝不要开始编写 HTML 页面。他们可能只想要文本答复、JSON 输出或简要摘要。请询问:“要我为此制作一个交互式 HTML 页面,还是文本摘要就够了?”
-
选择输出格式前先询问。 当用户的请求可以用文本、JSON 或可视化页面来回答时,请询问他们偏好哪种格式。不要擅自决定。
概述
功能齐全的命令行客户端,支持 所有 Google Maps Platform REST API。与基于浏览器的 google-maps 技能不同,本技能直接调用 Google 的 API,以快速获取结构化 JSON 响应。涵盖地图、路线、地点、环境和地理空间服务领域的 20 多个 API。
设置
1. 获取 Google Maps API 密钥
- 前往 Google Cloud Console
- 创建或选择一个项目
- 启用所需的 API(请参见下方的 API 列表)
- 前往 API 和服务 > 凭据,然后创建 API 密钥
- (建议)将密钥限制为仅可用于所需的 API
2. 保存 API 密钥
将其添加到以下任一位置的 .env 文件中(按顺序搜索):
# Option 1: Project directory .env
echo 'GOOGLE_MAPS_API_KEY=your_key_here' >> .env
# Option 2: Home directory .env
echo 'GOOGLE_MAPS_API_KEY=your_key_here' >> ~/.env
# Option 3: Environment variable
export GOOGLE_MAPS_API_KEY=your_key_here
3. 启用所需的 API
在 Google Cloud Console > API 和服务 > 库中,启用所需的 API:
| API 名称 | 控制台名称 |
|---|---|
| 地理编码 | Geocoding API |
| 路线 | Routes API |
| 地点 | Places API (New) |
| 海拔 | Elevation API |
| 时区 | Time Zone API |
| 空气质量 | Air Quality API |
| 花粉 | Pollen API |
| 太阳能 | Solar API |
| 天气 | Weather API |
| 地址验证 | Address Validation API |
| 道路 | Roads API |
| 街景 | Street View Static API |
| 静态地图 | Maps Static API |
| 地理定位 | Geolocation API |
| 航拍视图 | Aerial View API |
| 路线优化 | Route Optimization API |
| 地点聚合 | Places Insights API |
脚本位置
~/.claude/skills/google-maps-api/scripts/gmaps.py
无需外部依赖项——仅使用 Python 标准库(urllib、json、ssl)。
完整 API 参考
1. 地理编码
正向地理编码 - 将地址转换为坐标:
python3 ~/.claude/skills/google-maps-api/scripts/gmaps.py geocode "1600 Amphitheatre Parkway, Mountain View, CA"
python3 ~/.claude/skills/google-maps-api/scripts/gmaps.py geocode "Tokyo Tower" --language ja --region jp
python3 ~/.claude/skills/google-maps-api/scripts/gmaps.py geocode "Paris" --components "country:FR"
反向地理编码 - 将坐标转换为地址:
python3 ~/.claude/skills/google-maps-api/scripts/gmaps.py reverse-geocode 37.4224 -122.0856
python3 ~/.claude/skills/google-maps-api/scripts/gmaps.py reverse-geocode 48.8584 2.2945 --language fr
2. 路线和导航
获取两个地点之间的导航路线:
python3 ~/.claude/skills/google-maps-api/scripts/gmaps.py directions "New York, NY" "Boston, MA"
python3 ~/.claude/skills/google-maps-api/scripts/gmaps.py directions "LAX" "SFO" --mode transit
python3 ~/.claude/skills/google-maps-api/scripts/gmaps.py directions "Seattle" "Portland" --alternatives --avoid-tolls
python3 ~/.claude/skills/google-maps-api/scripts/gmaps.py directions "A" "D" --waypoints "B" "C"
python3 ~/.claude/skills/google-maps-api/scripts/gmaps.py directions "Home" "Work" --mode bicycling --units imperial
距离矩阵 - 多个起点/目的地:
python3 ~/.claude/skills/google-maps-api/scripts/gmaps.py distance-matrix \
--origins "New York" "Boston" \
--destinations "Philadelphia" "Washington DC"
3. 地点
文本搜索 - 根据查询查找地点:
python3 ~/.claude/skills/google-maps-api/scripts/gmaps.py places-search "best pizza in Chicago"
python3 ~/.claude/skills/google-maps-api/scripts/gmaps.py places-search "pharmacy" --location "40.714,-74.006" --radius 1000
python3 ~/.claude/skills/google-maps-api/scripts/gmaps.py places-search "5-star hotels" --min-rating 4.5 --open-now
python3 ~/.claude/skills/google-maps-api/scripts/gmaps.py places-search "EV charging" --type "electric_vehicle_charging_station"
附近搜索 - 查找坐标附近的地点:
python3 ~/.claude/skills/google-maps-api/scripts/gmaps.py places-nearby 40.7128 -74.0060 --type restaurant --radius 800
python3 ~/.claude/skills/google-maps-api/scripts/gmaps.py places-nearby 34.0522 -118.2437 --type cafe --max-results 5
地点详情 - 获取地点的完整信息:
python3 ~/.claude/skills/google-maps-api/scripts/gmaps.py place-details ChIJN1t_tDeuEmsRUsoyG83frY4
自动补全 - 输入时显示建议:
python3 ~/.claude/skills/google-maps-api/scripts/gmaps.py autocomplete "star" --location "37.7749,-122.4194"
python3 ~/.claude/skills/google-maps-api/scripts/gmaps.py autocomplete "central p" --types "park"
地点照片 - 获取照片 URL:
python3 ~/.claude/skills/google-maps-api/scripts/gmaps.py place-photo "places/PLACE_ID/photos/PHOTO_REF" --max-width 800
4. 空气质量
当前状况:
python3 ~/.claude/skills/google-maps-api/scripts/gmaps.py air-quality 40.7128 -74.0060
python3 ~/.claude/skills/google-maps-api/scripts/gmaps.py air-quality 40.7128 -74.0060 --health --pollutants
历史数据(最多 30 天):
python3 ~/.claude/skills/google-maps-api/scripts/gmaps.py air-quality-history 40.7128 -74.0060 --hours 48
预测(最多 96 小时):
python3 ~/.claude/skills/google-maps-api/scripts/gmaps.py air-quality-forecast 40.7128 -74.0060
5. 花粉
花粉预测(最多 5 天,草类/杂草/树木):
python3 ~/.claude/skills/google-maps-api/scripts/gmaps.py pollen 40.7128 -74.0060
python3 ~/.claude/skills/google-maps-api/scripts/gmaps.py pollen 34.0522 -118.2437 --days 5
返回:3 种植物类型和 15 个物种的通用花粉指数(UPI)。
6. 太阳能
建筑太阳能潜力:
python3 ~/.claude/skills/google-maps-api/scripts/gmaps.py solar 37.4219 -122.0841
python3 ~/.claude/skills/google-maps-api/scripts/gmaps.py solar 37.4219 -122.0841 --quality HIGH
返回:屋顶面积、日照时长、最佳面板布局、能源/成本估算。
太阳能数据图层(DSM、通量、阴影栅格):
python3 ~/.claude/skills/google-maps-api/scripts/gmaps.py solar-layers 37.4219 -122.0841 --radius 100
7. 天气
当前状况:
python3 ~/.claude/skills/google-maps-api/scripts/gmaps.py weather 40.7128 -74.0060
python3 ~/.claude/skills/google-maps-api/scripts/gmaps.py weather 40.7128 -74.0060 --mode current
每小时预测(最多 240 小时):
python3 ~/.claude/skills/google-maps-api/scripts/gmaps.py weather 40.7128 -74.0060 --mode hourly --hours 48
每日预测(最多 10 天):
python3 ~/.claude/skills/google-maps-api/scripts/gmaps.py weather 40.7128 -74.0060 --mode daily --days 7
近期历史(最多 24 小时):
python3 ~/.claude/skills/google-maps-api/scripts/gmaps.py weather 40.7128 -74.0060 --mode history --hours 12
8. 海拔
python3 ~/.claude/skills/google-maps-api/scripts/gmaps.py elevation 39.7392 -104.9903
python3 ~/.claude/skills/google-maps-api/scripts/gmaps.py elevation --locations "39.7392,-104.9903|36.4555,-116.8666"
python3 ~/.claude/skills/google-maps-api/scripts/gmaps.py elevation --path "36.578,-118.292|36.606,-118.099" --samples 20
9. 时区
python3 ~/.claude/skills/google-maps-api/scripts/gmaps.py timezone 40.7128 -74.0060
python3 ~/.claude/skills/google-maps-api/scripts/gmaps.py timezone 35.6762 139.6503 --language ja
10. 地址验证
python3 ~/.claude/skills/google-maps-api/scripts/gmaps.py validate-address "1600 Amphitheatre Pkwy, Mountain View, CA 94043"
python3 ~/.claude/skills/google-maps-api/scripts/gmaps.py validate-address "123 Main St" --region US --enable-usps
返回:可投递性判定、修正后的地址、组件级确认、USPS CASS 数据。
11. 道路
贴合道路 - 对齐 GPS 轨迹:
python3 ~/.claude/skills/google-maps-api/scripts/gmaps.py snap-roads "60.170,-24.942|60.171,-24.941|60.172,-24.940"
python3 ~/.claude/skills/google-maps-api/scripts/gmaps.py snap-roads "60.170,-24.942|60.172,-24.940" --interpolate
最近道路:
python3 ~/.claude/skills/google-maps-api/scripts/gmaps.py nearest-roads "60.170,-24.942|60.171,-24.941"
12. 街景
CLI 数据使用(服务端下载图像,费用为每 1,000 次 $7):
python3 ~/.claude/skills/google-maps-api/scripts/gmaps.py streetview --lat 46.414 --lng 10.013 --heading 90
python3 ~/.claude/skills/google-maps-api/scripts/gmaps.py streetview --location "Eiffel Tower, Paris" --size 800x600
python3 ~/.claude/skills/google-maps-api/scripts/gmaps.py streetview --pano CAoSLEFGMVFpcE... --output paris_sv.jpg
用于 HTML 页面——请始终改用 Google Maps 直接链接(零费用,不会暴露密钥):
https://www.google.com/maps/@?api=1&map_action=pano&viewpoint={lat},{lng}&heading={heading}&pitch=0&fov=90
详情请参阅下方的“HTML 中的街景”部分。
**警告:**请勿使用旧的简写格式 @{lat},{lng},3a,75y,{heading}h,90t——它不可靠,通常会打开缩小后的世界地图,而不是街景。请始终使用上面的 map_action=pano 格式。
13. 静态地图
python3 ~/.claude/skills/google-maps-api/scripts/gmaps.py static-map --lat 40.714 --lng -74.006 --zoom 13
python3 ~/.claude/skills/google-maps-api/scripts/gmaps.py static-map --center "Tokyo" --maptype satellite --zoom 12
python3 ~/.claude/skills/google-maps-api/scripts/gmaps.py static-map --center "NYC" --markers "color:red|40.714,-74.006" --size 800x600
14. 地理定位
python3 ~/.claude/skills/google-maps-api/scripts/gmaps.py geolocation
python3 ~/.claude/skills/google-maps-api/scripts/gmaps.py geolocation --wifi "00:11:22:33:44:55,-65" "66:77:88:99:AA:BB,-72"
15. 航拍视图(仅限美国)
python3 ~/.claude/skills/google-maps-api/scripts/gmaps.py aerial-view check --address "1600 Amphitheatre Pkwy, Mountain View"
python3 ~/.claude/skills/google-maps-api/scripts/gmaps.py aerial-view render --address "1600 Amphitheatre Pkwy, Mountain View"
python3 ~/.claude/skills/google-maps-api/scripts/gmaps.py aerial-view get --video-id VIDEO_ID
16. 路线优化
求解车辆路径问题(VRP):
python3 ~/.claude/skills/google-maps-api/scripts/gmaps.py route-optimize problem.json --project my-gcp-project
输入 JSON 格式:根据 Google Route Optimization API 规范,使用 {"model": {"shipments": [...], "vehicles": [...]}}。
17. 地点聚合(洞察)
统计或列出某个区域内符合筛选条件的地点:
python3 ~/.claude/skills/google-maps-api/scripts/gmaps.py places-aggregate --location "40.714,-74.006" --radius 5000 --type restaurant
python3 ~/.claude/skills/google-maps-api/scripts/gmaps.py places-aggregate --location "34.052,-118.244" --type cafe --min-rating 4.0 --insight INSIGHT_PLACES
18. Maps 嵌入网址(免费)
生成可嵌入的地图网址(免费且不限量):
python3 ~/.claude/skills/google-maps-api/scripts/gmaps.py embed-url --mode place --query "Eiffel Tower"
python3 ~/.claude/skills/google-maps-api/scripts/gmaps.py embed-url --mode directions --origin "NYC" --destination "Boston"
python3 ~/.claude/skills/google-maps-api/scripts/gmaps.py embed-url --mode streetview --lat 46.414 --lng 10.013
Claude 的使用模式
当用户询问位置、地理或环境问题时,请使用相应的命令:
| 用户意图 | 命令 |
|---|---|
| “……在哪里?” | geocode |
| “……这个地址是什么?” | reverse-geocode |
| “从 A 到 B 怎么走?” | directions |
| “A 到 B 有多远?” | distance-matrix |
| “查找附近的餐厅……” | places-search 或 places-nearby |
| “……的营业时间是什么?” | place-details |
| “……的空气质量怎么样?” | air-quality |
| “今天花粉严重吗?” | pollen |
| “……的天气怎么样?” | weather |
| “这个地址有效吗?” | validate-address |
| “……的海拔是多少?” | elevation |
| “……属于哪个时区?” | timezone |
| “给我看看……的地图” | static-map |
| “我能在屋顶上安装太阳能板吗?” | solar |
| “优化配送路线” | route-optimize |
| “这个区域内有多少家咖啡店?” | places-aggregate |
交互式 HTML 输出
重要:生成 HTML 前务必先询问。 未经用户明确同意,绝不要开始编写 HTML 文件。以文本形式交付结果后,请询问:
“要我把这些内容整理成交互式 HTML 页面,方便你在浏览器中打开吗?默认主题为 Warm Stone Sunrise(浅色、暖色调、高级感)。”
如果用户同意(或明确要求生成 HTML、页面或地图),再生成。如果用户没有回应或表示拒绝,就只提供文本或 JSON 结果。
默认情况下,请使用 Google Maps 嵌入式 iframe(output=embed)生成无需密钥的 HTML 页面——无需 API 密钥,也没有密钥暴露风险。只有当用户明确要求嵌入式地图无法支持的高级交互功能(如自定义标记、折线、聚类等)时,才使用 Maps JavaScript API(<script src="https://maps.googleapis.com/maps/api/js?key=...">)。
零密钥嵌入式 iframe(默认)
地图请使用以下 iframe 格式——无需 API 密钥,且免费:
地点/位置地图:
<iframe src="https://maps.google.com/maps?q=Oahu+Hawaii&z=10&output=embed"
width="100%" height="400" style="border:0" allowfullscreen></iframe>
路线地图:
<iframe src="https://maps.google.com/maps?saddr=Los+Angeles+CA&daddr=San+Jose+CA&output=embed"
width="100%" height="400" style="border:0" allowfullscreen></iframe>
参数:
q— 地点名称或地址(URL 编码,空格用+表示)saddr/daddr— 路线的起点/终点z— 缩放级别(1-20,默认约为 12)output=embed— 必填,使地图可嵌入ll— 可选的中心坐标lat,lng
警告: Google Maps 嵌入式 iframe 中请勿使用 loading="lazy"。延迟加载会阻止屏幕外的 iframe 加载,导致首屏下方的地图永久显示为空白。请始终省略 loading 属性,或使用 loading="eager"。
| 结果类型 | 默认 HTML 元素 |
|---|---|
| 街景 | 直接使用 Google Maps 链接(map_action=pano)——在新标签页中打开完整的交互式街景。请参阅“HTML 中的街景”部分。 |
| 路线 | 使用带有 saddr / daddr 和 output=embed 的嵌入式 iframe |
| 地点搜索 | 使用带有 q=place+name 和 output=embed 的嵌入式 iframe,并附带信息卡片 |
| 附近搜索 | 使用以地点为中心的嵌入式 iframe,并附带地点卡片 |
| 静态地图 | 使用带有 q 和 z 的嵌入式 iframe(可缩放、可拖动) |
| 天气/空气质量 | 使用地点的嵌入式 iframe,并附带状况卡片、图标和图表 |
| 海拔 | 海拔剖面图表 + 带有路径标记的嵌入式 iframe |
| 太阳能 | 使用建筑物位置的嵌入式 iframe,并附带太阳能潜力统计数据 |
| 行程规划 | 组合式多区块页面:嵌入式 iframe 地图、地点卡片、天气组件 |
生成 HTML 页面时:
- 使用单个独立的
.html文件(内联 CSS/JS,无外部依赖) - 默认对所有地图使用零密钥嵌入式 iframe(
output=embed)——HTML 中不得包含 API 密钥 - **HTML 页面中绝不可使用
google.maps.StreetViewPanorama或街景 JS API。**街景始终使用带有map_action=pano的直接 Google Maps 链接。这是硬性规则。 - **除非用户明确要求高级交互功能,否则绝不可包含 Google Maps JS API
<script>标签。**使用output=embed的 iframe 方案即可满足大多数用例,无需任何 API 密钥。 - 使用描述性名称保存到当前工作目录(例如
marea-streetview.html、nyc-trip-plan.html) - 创建完成后,通过
open <file>(macOS)自动在浏览器中打开
HTML 中的 Street View — 零密钥暴露
**硬性规则:绝不要在 HTML 页面中使用 JavaScript API 或 Embed API 嵌入 Street View。**这两种方式都会在客户端代码中暴露 API 密钥。应始终使用直接的 Google Maps 链接,在用户的浏览器中打开完整的交互式 Street View 体验——零成本,零 API 密钥暴露。
直接链接格式(Google Maps URLs API — 可靠):
https://www.google.com/maps/@?api=1&map_action=pano&viewpoint={lat},{lng}&heading={heading}&pitch={pitch}&fov=90
参数:
viewpoint={lat},{lng}— Street View 位置的坐标heading— 罗盘方向角度(0=北,90=东,180=南,270=西)pitch— 俯仰角(-90=向下,0=水平,90=向上)fov— 视野角度(10-100,默认值为 90)map_action=pano— 必需 — 显式触发 Street View 全景模式
**警告:**不要使用旧的简写格式 @{lat},{lng},3a,75y,{heading}h,{pitch}t——它不可靠,经常无法打开 Street View,而是显示缩小的世界地图。始终使用 map_action=pano 格式。
示例:
https://www.google.com/maps/@?api=1&map_action=pano&viewpoint=40.76545,-73.98115&heading=90&pitch=0&fov=90
https://www.google.com/maps/@?api=1&map_action=pano&viewpoint=48.8584,2.2945&heading=180&pitch=0&fov=90
https://www.google.com/maps/@?api=1&map_action=pano&viewpoint=46.414,10.013&heading=270&pitch=-5&fov=90
如何在 HTML 页面中实现:
独立的 Street View 按钮:
<a href="https://www.google.com/maps/@?api=1&map_action=pano&viewpoint=40.76545,-73.98115&heading=90&pitch=0&fov=90"
target="_blank" rel="noopener noreferrer"
style="display:inline-block; padding:12px 24px; background:#4f46e5; color:white;
border-radius:8px; text-decoration:none; font-weight:600;">
Open Street View →
</a>
JavaScript 函数(例如,用于包含多个地点的旅行计划页面):
function openStreetView(lat, lng, name) {
window.open(
`https://www.google.com/maps/@?api=1&map_action=pano&viewpoint=${lat},${lng}&heading=0&pitch=0&fov=90`,
'_blank'
);
}
带有背景信息的卡片/预览:
<div class="streetview-card">
<div class="sv-preview">
<div class="sv-overlay">
<span class="sv-icon">🔭</span>
<span>Interactive Street View</span>
</div>
</div>
<a href="https://www.google.com/maps/@?api=1&map_action=pano&viewpoint={lat},{lng}&heading=0&pitch=0&fov=90"
target="_blank" rel="noopener noreferrer" class="sv-button">
Open in Google Maps →
</a>
<p class="sv-note">Opens full 360° interactive Street View — pan, zoom, and walk around</p>
</div>
在整合后的旅行计划页面中,将原有的 StreetViewPanorama 部分替换为样式化的按钮/链接卡片。用户点击后即可在新标签页中打开完整的 Street View——直接通过 Google Maps 获得完整的交互式体验。
为何采用这种方式:
- 零 API 密钥暴露——HTML 源代码中完全不包含密钥
- 零成本——没有 API 调用,也不产生费用
- 体验更佳——可使用带导航功能的完整 Google Maps Street View,而不是功能有限的嵌入内容
- 适用于所有部署模式(个人、自带密钥、平台密钥)
一站式整合页面
对于包含多个部分的请求(旅行计划、地点调研、比较),始终主动提供生成一个整合后的 HTML 页面,将收集到的所有数据汇总到一个交互式仪表板中。用户应当能够一次查看所有内容,而不是滚动浏览终端输出。
整合页面应包含对话期间收集的每一项数据,例如,旅行计划页面可以整合:
- 带有驾驶路线折线的交互式路线地图
- 目的地天气组件
- 带有照片、评分、营业时间的酒店/地点卡片
- 附有 Street View 链接的餐厅推荐(可在 Google Maps 中打开完整的 360° 视图)
- 时间线/行程安排部分
- 租车与机场信息
整合页面的设计原则:
- 使用简洁、现代的仪表板布局,并划分清晰的区块
- 每个区块都应有丰富的视觉内容:地图、卡片、图标、徽章,而不只是文字
- 包含平滑滚动导航(带有活动链接高亮的粘性磨砂玻璃导航栏)
- 滚动显示动画(通过 IntersectionObserver 为每个区块实现向上淡入效果)
- 完全响应式:桌面端(>1024px)、平板端(641-1024px)、移动端(<=640px)、小屏手机(<=380px)
- 在移动端,卡片、按钮、旅行标签和统计网格改为单列布局
- 在移动端缩小地图高度(280px),并让按钮占满宽度
默认主题:暖石日出
除非用户另有要求,否则所有 HTML 页面都默认使用此主题。
配色方案:
- 背景:
#fafaf9(暖调米白) - 表面:卡片使用
#ffffff,替代表面使用#f5f5f4 - 边框:
#e7e5e4(暖石色),#f0eeec(浅色分隔线) - 文本:
#1c1917(主要)、#57534e(次要)、#a8a29e(第三级) - 强调色:
#4f46e5(靛蓝),搭配浅色#eef2ff和中间色#c7d2fe - 语义色:绿色
#059669、琥珀色#d97706、红色#dc2626、蓝色#2563eb
排版:
- 标题:
Playfair Display(衬线字体,字重 700-900)——营造精致的编辑风格 - 正文:
Inter(无衬线字体),启用完整字体平滑 - 区块模式:大写标签(11px,强调色)→ 衬线标题(26px)→ 描述(14px,第三级)
主视觉: linear-gradient(160deg, #eef2ff, #faf5ff, #fff7ed)——从靛蓝过渡到薰衣草色,再到蜜桃色
地图: 默认通过嵌入式 iframe 使用 Google Maps 样式。选择使用 Maps JS API 时,应用浅色自定义样式——柔和的石色调、淡蓝色水域 #c9d7e4、绿色公园 #d4e9d4、白色道路
阴影: 基础阴影柔和,为 0 1px 2px rgba(0,0,0,0.04);悬停时使用 0 4px 6px
导航: 粘性磨砂玻璃效果(rgba(255,255,255,0.85) + backdrop-filter: blur(20px)),活动链接带下划线动画
交互: 卡片悬停时上移(translateY(-2px))、时间轴圆点填充并带光环、步骤悬停时缩进、按钮悬停时上移并增强阴影
API 密钥安全与生产架构
问题
嵌入客户端 HTML/JavaScript 的任何 API 密钥都可由用户通过浏览器开发者工具查看。这对于个人使用没有问题,但对于服务多个用户的生产应用来说,至关重要。
部署模式
此技能支持三种安全级别不同的部署模式:
模式 1:个人 / CLI 使用(单用户)
用户自己的 API 密钥存放在 .env 中,由 gmaps.py 在本地使用。密钥不会离开本机。
User's Machine
├─ .env (GOOGLE_MAPS_API_KEY=...)
├─ gmaps.py → calls Google APIs directly
└─ HTML pages → zero-key embed iframes (preferred) or key embedded (acceptable)
风险:低——这是用户自己的密钥,存放在自己的机器上。
密钥限制:无需设置(也可以限制为你所用的 API)。
最佳实践:即使是个人使用,HTML 页面也应优先使用零密钥的 output=embed iframe。只有在需要高级功能(自定义标记、折线、聚类)时,才使用 Maps JS API。
模式 2:用户自带密钥(多用户应用)
用户配置自己的 Google API 密钥,并自行承担费用。
Your App
├─ User configures their own API key in app settings
├─ Key stored in user's profile (encrypted at rest)
├─ Used server-side for data queries
└─ Frontend key: user creates a separate domain-restricted key
风险:低至中等——这是用户自己的密钥,由用户承担费用。 重要:生成可分享 / 导出的 HTML 页面时,绝不要嵌入用户的密钥。应改用静态导出(参见下文的“可分享导出模式”)。 引导流程:指导用户完成 GCP Console 设置和 API 启用(参见上文“设置”部分)。
模式 3:平台密钥(用户向你付费)——至关重要
用户向你的平台付费。由你提供 Google API 密钥。用户绝不能看到该密钥。
架构:双密钥 + 后端代理
┌─────────────────────────────────────────────────────┐
│ User's Browser │
│ │
│ HTML Page (served from app.yourdomain.com) │
│ ├─ Interactive Maps ← Frontend Key (domain-locked) │
│ ├─ Street View ← Frontend Key (domain-locked) │
│ └─ All data (weather, places, directions, etc.) │
│ ↑ pre-rendered from backend, NO key in browser │
│ │
└──────────────────┬────────────────────────────────────┘
│ /api/geocode, /api/directions, etc.
▼
┌──────────────────────────────────────────────────────┐
│ YOUR Backend Server │
│ │
│ Backend Key (env var, never sent to browser) │
│ ├─ Geocoding, Directions, Places, Weather, etc. │
│ ├─ Street View Static → returns image bytes │
│ └─ All data APIs proxied with your key server-side │
│ │
│ Key restricted by: Server IP address │
│ Even if leaked: won't work from other IPs │
└──────────────────┬────────────────────────────────────┘
│
▼
Google Maps Platform
前端密钥(客户端,在 HTML 中):
- 仅启用:Maps JavaScript API(用于交互式地图渲染)
- 限制方式:HTTP 引用来源 →
https://app.yourdomain.com/* - 即使有人复制该密钥,也只能从你的域名使用
- 无法调用数据 API(地理编码、地点等)——这些 API 在此密钥上已禁用
后端密钥(服务器端,隐藏):
- 启用:所有数据 API(地理编码、路线、地点、天气、花粉、太阳能等)
- 限制方式:服务器 IP 地址
- 永远不会发送到浏览器——仅保存在服务器环境变量中
- 即使泄露,也只能通过服务器的 IP 使用
此模式下 HTML 页面的工作方式:
// UNSAFE — key exposed, data fetched client-side:
const service = new google.maps.DirectionsService();
service.route({ origin: 'JFK', destination: 'The Plaza' });
// SAFE — data pre-fetched by backend, only map rendering in browser:
const routeData = /* injected by server from backend API call */;
const path = google.maps.geometry.encoding.decodePath(routeData.polyline);
new google.maps.Polyline({ path: path, map: map });
在 GCP Console 中设置两个密钥
步骤 1:创建前端密钥
- 在 GCP Console 中前往 APIs & Services > Credentials
- 点击 Create Credentials > API Key
- 点击该密钥进行编辑
- 在 Application restrictions 下:选择 HTTP referrers
- 添加你的域名:
https://app.yourdomain.com/* - 在 API restrictions 下:选择 Restrict key
- 仅启用:Maps JavaScript API
- 保存
步骤 2:创建后端密钥
- 再次点击 Create Credentials > API Key
- 点击该密钥进行编辑
- 在 Application restrictions 下:选择 IP addresses
- 添加你的服务器 IP 地址
- 在 API restrictions 下:选择 Restrict key
- 启用:Geocoding、Routes、Places (New)、Weather、Air Quality、Pollen、Solar、Elevation、Time Zone、Address Validation、Roads、Street View Static、Geolocation、Aerial View、Route Optimization
- 保存
步骤 3:配置你的应用
# Backend server environment
GOOGLE_MAPS_BACKEND_KEY=AIzaSy...xxx # Server-side only, IP-restricted
GOOGLE_MAPS_FRONTEND_KEY=AIzaSy...yyy # Injected into HTML, domain-restricted
可分享导出模式
当用户想要下载或分享 HTML 页面(电子邮件、Slack 等)时,生成不含任何 API 密钥的静态导出:
| 元素 | 应用内(交互式) | 可分享导出 |
|---|---|---|
| 地图 | Maps JavaScript API(前端密钥) | Maps Embed API <iframe>(免费,没有密钥滥用风险) |
| 街景 | 直接使用 Google Maps 链接(不需要密钥) | 直接使用 Google Maps 链接(不需要密钥) |
| 路线 | DirectionsRenderer(前端密钥) | 带有路径叠加层的静态地图图像(base64) |
| 数据(天气、地点) | 从后端预先渲染 | 相同的预渲染 HTML——不需要 API 调用 |
| API 密钥暴露 | 前端密钥,受域名限制 | 完全没有密钥 |
生成嵌入 URL(免费、无限制):
python3 ~/.claude/skills/google-maps-api/scripts/gmaps.py embed-url --mode place --query "Marea Restaurant NYC"
python3 ~/.claude/skills/google-maps-api/scripts/gmaps.py embed-url --mode directions --origin "JFK" --destination "The Plaza Hotel NYC"
python3 ~/.claude/skills/google-maps-api/scripts/gmaps.py embed-url --mode streetview --lat 40.76545 --lng -73.98115
安全摘要
| 部署方式 | 数据 API | 地图渲染 | 密钥暴露 | 可分享 |
|---|---|---|---|---|
| 个人 CLI | 用户的密钥,位于本地 | 零密钥嵌入式 iframe(默认) | 无 | 是(无密钥) |
| 个人 CLI(高级) | 用户的密钥,位于本地 | Maps JS API(选择启用折线、聚类等) | 用户自行承担风险 | 否(密钥会泄露) |
| BYOK(用户的密钥) | 用户的密钥,位于服务器上 | 用户创建前端密钥 | 用户自行承担风险 | 仅静态导出 |
| 平台密钥(由你付费) | 后端密钥(锁定 IP) | 前端密钥(锁定域名) | 安全 | 仅静态导出 |
速率限制与滥用防护(平台模式)
使用你的密钥为多个用户提供服务时,还应实施以下措施:
- 按用户进行速率限制:限制每位用户每小时/每天的 API 调用次数
- 身份验证:只有经过身份验证且付费的用户才能触发 API 调用
- 用量跟踪:记录触发各 API 调用的用户,以便计费
- 配额提醒:设置 GCP 账单提醒,以便发现意外峰值
- 预算上限:在 GCP Console 中设置每日最高支出
错误处理与 API 设置引导
重要:任何命令失败时,必须立即告知用户。 不要悄悄改用网页搜索或其他变通方案。用户需要知道哪里出了问题,才能决定如何修复。
命令失败时,检测错误类型并作出相应响应:
- 403 Forbidden / API 未启用:停止。告知用户,并提供 Playwright 引导操作(见下文)
- REQUEST_DENIED:停止。告知用户,并提供 Playwright 引导操作(见下文)
- 400 Bad Request:参数无效——检查 API 文档
- 429 Rate Limited:请求过多——加入短暂延迟
- ZERO_RESULTS:未找到结果——尝试扩大搜索范围
- INVALID_REQUEST:缺少必需参数
- 未找到密钥:确保已在
.env或环境变量中设置GOOGLE_MAPS_API_KEY - RefererNotAllowedMapError:前端密钥的 HTTP 引荐来源限制与当前域名不匹配
引导式 API 启用(Playwright 演示)
当 API 尚未启用时,这是首选响应。 不要跳过,也不要悄悄绕过此问题。
当用户的 API 调用因 403、REQUEST_DENIED 或“API 尚未启用”错误而失败时:
-
用通俗的语言告诉用户发生了什么:
"看起来您的 Google Cloud 项目尚未启用 [API Name]。没关系——我可以现在就在您的浏览器中引导您完成启用。要我继续吗?"
-
如果用户同意,使用 Playwright MCP 自动完成启用:
Step 1: Navigate to the specific API's enablement page: https://console.cloud.google.com/apis/library/[API_ENDPOINT] Step 2: If a project selector appears, let the user pick their project (or auto-select if there's only one) Step 3: Click the "Enable" button Step 4: Wait for the confirmation (loading spinner → "API enabled" or the button changes to "Manage") Step 5: Confirm to the user: "Done! [API Name] is now enabled. Let me retry your request." Step 6: Retry the original command automatically -
Playwright 导航所用的 API 端点 URL(使用以下确切路径):
API GCP Library URL 路径 Geocoding geocoding-backend.googleapis.comRoutes routes-backend.googleapis.comPlaces (New) places-backend.googleapis.comElevation elevation-backend.googleapis.comTime Zone timezone-backend.googleapis.comAir Quality airquality.googleapis.comPollen pollen.googleapis.comSolar solar.googleapis.comWeather weather.googleapis.comAddress Validation addressvalidation.googleapis.comRoads roads.googleapis.comStreet View Static street-view-image-backend.googleapis.comMaps Static static-maps-backend.googleapis.comMaps JavaScript maps-backend.googleapis.comMaps Embed maps-embed-backend.googleapis.comGeolocation geolocation.googleapis.comAerial View aerialview.googleapis.comRoute Optimization routeoptimization.googleapis.comURL 完整格式:
https://console.cloud.google.com/apis/library/[path_from_table] -
如果用户拒绝(希望手动操作),请提供直接链接:
"没问题!这是直接链接——点击 Enable 即可: https://console.cloud.google.com/apis/library/[API_ENDPOINT]"
-
批量启用:如果用户是首次设置,且需要启用多个 API,请主动提出一次性全部启用:
"我可以一次性为您启用所有常用 API:Geocoding、Routes、Places、Weather、Elevation、Time Zone 和 Maps JavaScript。要我帮您全部设置好吗?"
然后通过 Playwright 逐个访问各 API 页面,并点击 Enable。
-
Playwright 演示的重要说明:
- 用户必须已在浏览器中登录 Google Cloud Console
- 如果尚未登录,请告知他们先访问 https://console.cloud.google.com 登录
- 某些 API(Weather、Solar)可能要求项目启用结算功能
- 启用后可能需要约 30 秒才能生效
- 如果看不到 Enable 按钮,而是显示 Manage,则说明该 API 已启用
定价说明
大多数 API 按请求收费。主要免费/付费档位:
- Maps Embed API:始终免费(可用于生成可分享的导出内容)
- Maps JavaScript API:每 1,000 次加载收费 $7
- Street View (JS):每 1,000 次全景加载收费 $7——绝不要在 HTML 页面中使用;请改用直接的 Google Maps 链接(免费,无需密钥)
- Google Maps Platform 每月提供 $200 免费额度
- Routes API:每 1,000 次请求收费 $5-15,具体取决于功能
- Places API:每 1,000 次请求收费 $17-40
- Environment API(Air Quality、Pollen、Solar):价格不一
- 请查看 Google Maps 定价了解当前费率
说明
- 所有输出均为 JSON(图片下载除外,图片会保存为文件)
- 无需外部 Python 依赖——仅使用标准库
- 脚本会在以下位置查找
.env:CWD、主目录、skill 目录 - 对于图片 API(streetview、static-map),文件会保存在本地
- Weather API 可能需要单独启用结算功能
- Aerial View 仅支持美国地址
- Route Optimization 需要 GCP 项目 ID
- Roads 的限速功能需要 Asset Tracking 许可证