当前位置: > 首页 > 美食烹饪

用 AI + 高德地图 MCP,3 小时做出杭州美食地图

 

最近刷到一篇热帖,一位在阿里五年的同学总结了一份她这五年(吃的每一口记录)收集的杭州美食排行榜,为“美食荒漠”正名。也让我回忆起自己初到杭州进入团队时也曾想整理一份“美食地图”,却因种种原因而作罢。恰逢最近团队准备选团建的餐厅,于是立马把这篇帖子分享给投食官。投食官仔细调研后感慨道,“这篇文章真是太好了,如果能在地图上看到它们的位置就更好了。”

这让我萌生了为这个美食排行榜做一个杭州美食地图的想法,以便能快速地看到餐厅的位置并进行选择。实现方法上 AI coding 加高德 MCP 有没有搞头?

文末附源码和项目地址供大家参考和取用。

安装高德MCP server

过程参考 :Cursor快速接入高德MCP server

https://lbs.amap.com/api/mcp-server/gettingstarted

只需准备好你的高德 key,在 cursor 上设置里如下配置即可。

{   "mcpServers": {     "amap-amap-sse": {       "url""https://mcp.amap.com/sse?key=您在高德官网上申请的key"     }   } }

配置好后检查 mcpserver 的状态,如下图则是正常可用的。

给 AI 提需求-构建工程提示词

1. 在当前文件夹下进行编写项目,尽量少的创建文件,只创建必要文件。 2. 根据给定的杭州推荐的美食店,制作一份可以预览的美食地图。最终的产物使用便捷的方式呈现,如使用html进行展示。 3. 地图上标注下面每个店铺精确的位置,为每个类型的餐厅用不同的合适的图标表示。 4. 地图采用真实的杭州市地图。 5. 点击图标可显示餐厅更多信息,餐厅优势,预约电话,推荐指数等。 6. 过程中可以借助任何工具和能力实现这一目标,如 高德MCP工具。 7. 展示的店铺已给定的为准,最终给出的数据要真实可靠。 8.将下列数据,通过amp-amp-sse mcpserver,制作成适配高德地图的 json 格式数据。若没有找到餐厅,不要编造数据,坐标字段设为空值即可。 餐厅数据如下: 如*:主厨团队对传统杭帮菜的解构能力堪称艺术,招牌鱼圆弹润如凝脂,黄酒鹅肝配牛小排的组合既有东方韵味又有西式巧思。景观位虽为日式下沉设计,但私密性极佳,适合重要纪念日。唯一提醒:订位时建议直接对接主理人确认细节,米其林加身后服务响应速度有提升空间。 金*厅:四季酒店的镇店之宝,时令菜单每月更新,清明前的香椿嫩芽配河虾仁、霜降后的桂花糖藕都是必尝限定。脆皮鸡的表皮酥脆度堪比艺术品,和牛炒饭的镬气十足,适合带海外友人感受中式烹饪的精细度。 八*杯:里园的孪生品牌,早期的黄鱼锅贴仍是教科书级别,但近年创新菜式如松茸酿饭口感层次稍显单薄。建议避开周末午市,工作日晚间用餐能更好体会主厨团队的匠心。 集*楼:庭院里的江南美学展,青梅鹅肝配茉莉花茶冻的创意值得点赞。近期推出的「盲盒套餐」性价比突出,适合想体验高端杭菜又怕踩雷的新客。 木*溪:宋宴主题套餐需提前三天预订,茶点与菜肴的搭配暗合二十四节气,但每道菜量控制在2-3口刚好尝鲜。特别推荐茶艺师现场点茶,宋代七汤点茶法视觉效果震撼。 桂*房:满觉陇店的露台夜景是最大卖点,桂花酿醉虾的甜度把控精准。若多人聚餐可尝试「山房九宫格」拼盘,冷热荤素搭配考究,但需注意部分创意菜式如黑松露猫耳朵口味较重。 【浙菜】 锦*湖:原乾胧汇班底升级之作,龙翔桥店的脆皮鸡改良自港式烧味,外皮的焦糖层厚度比EFC店更均匀。推荐搭配店家自酿的青梅酒,解腻效果出众。 算*挡:滨江园区的宝藏食堂,人均150可吃到现拆的蟹粉豆腐和炭烤鳗鱼。建议4人以上拼单,把招牌菜凑齐分着吃更划算。 【创意菜】 雅*:西溪湿地内的隐秘据点,荷叶包蒸东山羊是夏季限定,肉质软烂入味。划船至码头需提前预约接驳船,整体体验像私人会所。 【融合菜】 乔*:阿里园区周边的老牌宴请场所,最近推出的「数字化点餐系统」可实时查看后厨烹饪进度。推荐黑椒牛排配龙井茶汤,西式主菜与杭帮调味的结合恰到好处。 略...

输入“需求文档”后,cursor 会进入 Plan 模式进行规划整体设计方案。

已开始为你的美食地图收集真实的高德地图数据。以下是实现方案和进度说明: 实现方案 数据收集:为每个餐厅通过高德地图API检索真实的地理位置(经纬度)、地址、照片等信息。若未找到则坐标为空。 数据格式:整理为适配高德地图MCP的JSON格式,包含必要的字段(如名称、类型、坐标、推荐指数、电话、优势等)。 地图展示:最终生成一个HTML文件,使用高德地图JS API进行可视化。不同类型餐厅用不同图标,点击可弹窗显示详细信息。 文件结构:只创建必要的文件(如index.html和数据JSON),保证项目简洁。 进度说明 已完成“如*”“金*厅”真实数据检索,获取了精确坐标和相关信息。 正在依次为所有餐厅检索真实数据,确保数据可靠。 检索不到的餐厅将坐标设为空,不会编造信息。 请确认: 是否需要为每家餐厅都补充“推荐指数、预约电话、优势”等字段?如无公开数据,是否允许留空或用描述性文字? 是否需要将所有数据单独导出为JSON文件,还是直接嵌入HTML中? 如无异议,将继续批量检索并整理数据,随后生成地图预览HTML。你可随时查看进度或指定展示细节。

十几分钟写完“需求文档”,晚饭时间到,提给 AI 让它先写着,我们去吃饭~

AI coding-开始寻找杭州美食之旅

调用高德 MCP Server-获取餐厅准确位置和信息

第一轮-模糊查询找到候选餐厅

AI 会先调用高德 MCP Server 的 maps_text_search 方法通过模糊查询,查到候选餐厅信息。

输入

{   "keywords""如院餐厅 杭州",   "city""杭州",   "citylimit"true }

输出

可以看到,查出了一系列餐厅,其中有如*餐厅的基本信息,取出 id,作为下一轮的输入。

{   "pois": [     ...     {       "id""B0JA5UF6R*",       "name""如*餐厅",       "address""玉泉路1*号",       "typecode""0501**",       "photo""http://store.is.autonavi.com/showpic/b08795ab6adeba25a0115d74130862**"     },      {       "id""B023B019A*",       "name""群*饭店(信*路店)",       "address""滨安路119*号(近信*路)",       "typecode""0501**",       "photo""http://store.is.autonavi.com/showpic/2ac9e95efffe6e27a8eda43ef82711**"     },     ...   ],   "suggestion": {     "keywords""",     "ciytes": {       "suggestion": []     }   } }

第二轮-通过 id 获取餐厅详情

可以看到紧接着 AI 会调用高德 MCP Server 的maps_search_detail能力,通过 id 来获取餐厅详情。

输入

{   "id""B0JA5UF6R*" }

输出

可以看到这里获取到了餐厅的详情信息,包含名称、经纬度、地址、照片、营业时间、评分等。

{"id":"B0JA5UF6R*","name":"如*餐厅","location":"120.122175,30.253356","address":"玉泉路1*号","business_area":"西湖旅游区中心片区","city":"杭州市","type":"餐饮服务;中餐厅;中餐厅","alias":"","photo":"http://store.is.autonavi.com/showpic/b08795ab6adeba25a0115d74130862a*","cost":"","opentime2":"周一至周日 11:30-14:00,17:30-21:00","rating":"4.7","open_time":"11:30-14:00 17:30-21:00","meal_ordering":"0"}

AI 会将高德 MCP Server 中获取的数据格式化,整理到 json 文件中,作为数据源。

编写页面

数据收集完后,就开始根据需求,编写代码了,通过引入高德 API 展示杭州地图,然后将上一步整理的数据引入进来,标出餐厅的位置。并且支持点击餐厅的名称,弹框展示餐厅的详情,餐厅的推荐理由。

虽然没有关注代码具体怎么实现的,但是它就这样水灵灵的工作起来了~

但我认为我们还是需要理解 AI 写的每一行代码,对代码质量有一个评估,一方面自己能有所进步,一方面不能让 AI 自由发挥的太过,到后面一行也看不懂,改也改不动的时候,项目就崩了。自己不理解的代码,可以让 AI 为代码都加上注释,方便理解。

调试页面

AI 写完了主体页面和功能点后,预览一下,还是有挺多细节需要修改的,如名称大小、背景框样式、弹框点击关闭逻辑等,这些和 AI 沟通,说明需要修改的位置和要求即可,如果说不清的时候,可以直接截图给 AI,告诉它要改什么,理解意图上还是十分强大的。

找素材

但是 AI 有一点不足的是不太方便从外部直接获取资源,比如试图让 AI 自己去网上找合适的素材图标,但是失败了,它会让我去给它下载,不然它会自己画一个丑陋抽象的 SVG 图标。

实在忍不了,只能去网上找素材,可以说整个过程中,花的最多的时间就是这里了。找合适的素材,尝试下载,要收费?换一个地方找,终于是免费的了,下载下来,传到图床上,变成公网可访问的链接。

整个找素材的过程十分痛苦,如果 AI 可以自己找就好了,也许是使用的姿势不太对,还需要继续研究。

好在最终找到了一个非常棒的平台,那就是阿里巴巴图标矢量库,还是自家兄弟好,素材非常的全且好用,大力推荐。

地址:https://www.iconfont.cn/?spm=a313x.search_index.i3.d4d0a486a.2dff3a81XH4Q3q

餐厅分类与筛选

对于之前的收集的餐厅的数据,没有一个很好的分类进行表达,于是让AI 重新分析一下整个餐厅数据,结合餐厅的信息,进行分类,然后将餐厅类型与图标素材再进行一个匹配。得到一个分类好的餐厅列表,与图标的映射关系。

然后就可以愉快的让 AI 结合这个分类数据,改写一下餐厅的图标啦,并且基于分类,再加上了图例列表,和筛选项的功能,方便展示,不然 100个餐厅全部展示在地图上,也不太方便小伙伴们看。

然后页面的效果如下:

AI 推荐餐厅

看了地图上标注的餐厅后,才发现,离自己园区的距离都比较远,于是含泪再写一份自己团队的聚餐选点指南,让 AI 帮助推荐餐厅。

1. 在当前项目中进行改进,删去图例、筛选项功能。 2. 使用给出的餐厅数据,在地图上进行标注。 3. 我们的出发位置是杭州市阿里巴巴滨江园区,标注出发点位置,用星星图标表示。 4. 图标素材在 resource/icon.txt中. 5. 计算出发点到各个餐厅的位置,在地图上用线连起来进行展示。 6. 分析一下各个餐厅的优劣,离出发点的距离,交通方式,和所需时间,周边配套,列在餐厅详情里。 7. 最后进行一个智能总结,推荐一个餐厅 8. 9. 过程中可以借助任何工具和能力实现这一目标,如 高德MCP工具。 10. 展示的店铺已给定的为准,最终给出的数据要真实可靠。 将下列数据,通过amp-amp-sse 的 mcpserver,制作成适配高德地图的 json 格式数据。若没有找到餐厅,不要编造数据,坐标字段设为空值即可。 1. 海*捞中南乐游城店 2. 黄*海鲜大排档(长河地铁站旁) 3. 群*饭店 4. 龙*·滨江烤全羊(杭州总店)dongda 5. 东*方保利时光里店

有了前面的基础,这次 20 分钟就搞定了,主要在写需求文档和微调页面样式上,可以分析一下各个餐厅的优劣,离出发点的距离,交通方式,和所需时间,周边配套等。 最后进行一个智能总结,推荐一个餐厅,效果如下:

小伙伴们就可以愉快的进行决策,到底选哪个餐厅了~

新的功能正在和我们投食官持续优化中,比如先选择团建地方,再选择聚餐地方,多个地点让 AI 分析推荐等~

发布与部署

公网部署方法挺多的,比如 GitHub、Gitee上提供了静态页面部署的能力,或者可以租一个阿里云服务器申请域名,搭建自己的网站。

在 GitHub 部署静态页面的简要指南

以下为 GitHub Pages 静态部署的核心步骤,基于官方文档和工具链实现:

1.创建仓库

新建公共仓库,命名格式为 username.github.iousername 为你的 GitHub 用户名)。克隆仓库到本地,添加静态文件(如 index.html),并推送至 main 分支。

2. 配置发布源

进入仓库 Settings > Pages,选择发布分支(如 maingh-pages)或 docs 文件夹作为源。若使用自定义构建工具(如 Hugo、Vue 等),需禁用默认 Jekyll 构建:在源目录根路径创建空文件 .nojekyll

3. 使用 GitHub Actions 自动部署

在工作流配置文件(如 .github/workflows/deploy.yml)中添加以下步骤:- name: Deploy to GitHub Pages     uses: peaceiris/actions-gh-pages@v3     with:       github_token: ${{ secrets.GITHUB_TOKEN }}       publish_dir: ./public  # 替换为你的构建输出目录

此 Action 会将指定目录内容部署到 gh-pages 分支。

4.访问页面

页面地址为 https://username.github.io,更新后通常需等待 1-10 分钟生效。

5. 注意事项

权限问题:若使用 GitHub Actions 推送,需确保管理员已验证邮箱并推送过代码。符号链接:仓库含符号链接时,需通过 GitHub Actions 部署。

最后

这次的实践我认为还是非常的有趣的,整个开发过程大概 3-4 小时,而且主要花在了找素材和调整样式上了。放在以前,有了一个好的点子,但是可能会因为麻烦,跨技术栈,生产周期长、拖延症等各种因素导致“创业未半而中道崩殂”。

不得不感叹,AI 真的能帮助人们提高效率,并且使之前不可能或者很难完成的事情变得可行。在 AI 时代,可能需要从之前传统的“关注技术栈和专业,关注如何实现”的思维转向聚焦到“扩展思维的宽度和认知,寻找事物的可达路径”上。

另外,高德的小伙伴是否考虑一下,增加一个餐厅、团建的地点投票功能~

可以在高德上发起投票,录入几个候选位置,邀请小伙伴们一起投票。可以很方便的查看当前位置(或者出发地点)距离候选地点的距离、交通方式和用时、候选地点周边配套等。 也可以让 AI 结合需求背景,总结一下各候选地点的优势。然后小伙伴们就可以愉快地投票了!

附录

源码如下,需要的小伙伴可自取,在这个基础上改改就能用了。

杭州美食地图

<!DOCTYPE html> <html lang="zh-CN"> <head>   <meta charset="UTF-8">   <title>杭州美食地图</title>   <meta name="viewport" content="width=device-width, initial-scale=1.0">   <style>     htmlbody#map { height100%margin0padding0; }     .amap-info-content { font-size14px; }     .legend {       position: absolute;       top10px;       left10px;       backgroundrgba(255,255,255,0.9);       border-radius6px;       padding10px;       box-shadow02px 8px rgba(0,0,0,0.15);       z-index1000;     }     .legend-item { display: flex; align-items: center; margin-bottom6px; }     .legend-icon { width20pxheight20pxmargin-right6px; }     .marker-label {       font-size16px;       color#fff;       backgroundrgba(34,34,34,0.75);       border-radius6px;       padding2px 10px;       box-shadow02px 8px rgba(0,0,0,0.15);       white-space: nowrap;       transition: background 0.2s;       border: none;     }     .marker-label-hover {       backgroundrgba(34,34,34,0.95);       color#ffe58f;       border: none;     }     /* 强制覆盖高德地图label的蓝色边框和背景 */     .amap-marker-label {       border: none !important;       box-shadow: none !important;       background: none !important;       padding0 !important;     }     .filter-panel {       position: absolute;       top40px;       right20px;       width220px;       backgroundrgba(255,255,255,0.97);       border-radius10px;       box-shadow02px 16px rgba(0,0,0,0.13);       padding18px 18px 12px 18px;       z-index1200;       font-familyPingFang SCMicrosoft YaHei, Arial, sans-serif;       font-size15px;       color#222;       transition: box-shadow 0.2s;       max-height600px;       overflow-y: auto;     }     .filter-title {       font-size18px;       font-weight: bold;       margin-bottom12px;       letter-spacing2px;     }     .filter-section { margin-bottom14px; }     .filter-label { font-size14pxmargin-bottom6pxcolor#666; }     .filter-recommend label.filter-category label {       display: inline-block;       margin-right10px;       margin-bottom6px;       cursor: pointer;       font-size14px;     }     .filter-category {       max-height280px;       overflow-y: auto;       border-radius6px;       background#f7f7f7;       padding6px 4px 4px 8px;       border1px solid #eee;     }     .filter-category label { width120px; }     .filter-panel input[type="checkbox"] {       accent-color#1890ff;       margin-right3px;       vertical-align: middle;     }   </style>   <!-- 高德地图 JS API -->   <script src="https://webapi.amap.com/maps?v=2.0&key=「你的高德 apikey」"></script> </head> <body>   <div id="map"></div>   <div class="legend" id="legend"></div>   <div id="filter-panel" class="filter-panel">     <div class="filter-title">杭州美食指南</div>     <div class="filter-section">       <div class="filter-label">推荐指数</div>       <div class="filter-recommend">         <label><input type="checkbox" name="recommend" value="5"> 5星</label>         <label><input type="checkbox" name="recommend" value="4"> 4星</label>         <label><input type="checkbox" name="recommend" value="3"> 3星</label>         <label><input type="checkbox" name="recommend" value="2"> 2星</label>         <label><input type="checkbox" name="recommend" value="1"> 1星</label>       </div>     </div>     <div class="filter-section">       <div class="filter-label">餐厅类别</div>       <div class="filter-category" id="filter-category-list"></div>     </div>   </div>   <script>     // 餐厅数据直接嵌入或读取同级目录下文件都可     const data =  [   {     "name""如*餐厅",     "type""中餐厅",     "location""120.122175,30.253356",     "address""玉泉路1*号",     "photo""http://store.is.autonavi.com/showpic/b08795ab6adeba25a0115d74130862aa",     "cost""",     "open_time""11:30-14:00 17:30-21:00",     "rating""4.7",     "phone""",     "advantage""推荐理由 xxxx",     "recommend_index""5",     "category""中餐"   },   {     "name""杭州**湖四季酒店金沙厅",     "type""浙江菜",     "location""120.129637,30.251887",     "address""灵隐路5号杭州**湖四季酒店1层(近曲院风荷)",     "photo""http://store.is.autonavi.com/showpic/03f435393b04359460529d3cc484e826",     "cost""533.00",     "open_time""11:30-14:00 17:30-21:00",     "rating""4.8",     "phone""",     "advantage""推荐理由 xxxx",     "recommend_index""5",     "category""杭帮菜/浙江菜"   },   {     "name""门没锁咖啡馆",     "type""咖啡馆",     "location""120.099355,30.297518",     "address""政紫弄23幢**号沿街(**地铁站F口步行210米)",     "photo""https://aos-comment.amap.com/B0KUPD8084/comment/A241830B_F0B4_4496_A253_95A1F5385C51_L0_001_1500_2000_1735672012627_76019873.jpg",     "cost""",     "open_time""10:00-17:00",     "rating""4.3",     "phone""",     "advantage""推荐理由 xxxx",     "recommend_index""4",     "category""咖啡馆"   }   {     "name""helloworld",     "type""咖啡馆",     "location""120.005480,30.280547",     "address""欧美金融城西溪丽晶居*幢底商1-13号",     "photo""http://store.is.autonavi.com/showpic/72efe174477c732d4264f7373dfaf768",     "cost""30.00",     "open_time""08:00-18:00",     "rating""4.3",     "phone""",     "advantage""推荐理由 xxxx",     "recommend_index""4",     "category""咖啡馆"   } ];     // 图标映射表,按最新icon.txt     const typeIcons = {       辣椒「需要自己准备」,       甜点「需要自己准备」,       咖啡「需要自己准备」,       中餐「需要自己准备」,       日料「需要自己准备」,       快餐「需要自己准备」,       烤肉「需要自己准备」,       烧烤「需要自己准备」,       星星「需要自己准备」,       火锅「需要自己准备」,       牛排「需要自己准备」,       面食「需要自己准备」,       海鲜「需要自己准备」,       韩餐「需要自己准备」,       小吃「需要自己准备」,       粤菜「需要自己准备」,       default「需要自己准备」     };     // 图例分类,按icon.txt顺序     const legendTypes = [       {type中餐label中餐/杭帮菜/其他},       {type粤菜label粤菜},       {type辣椒label川菜},       {type火锅label火锅},       {type烧烤label烧烤},       {type烤肉label烤肉},       {type日料label日料},       {type韩餐label韩餐},       {type牛排label西餐},       {type面食label面馆/面食},       {type小吃label小吃},       {type甜点label甜品/饮品},       {type咖啡label咖啡馆},       {type快餐label快餐},       {type海鲜label海鲜}     ];     // category到icon的映射规则     function getCategoryIcon(category){       if (!category) return typeIcons[default];       if (category === 中餐 || category === 杭帮菜/浙江菜 || category === 湘菜 || category === 东北菜 || category === 素食 || category === 私房菜return typeIcons[中餐];       if (category === 粤菜return typeIcons[粤菜];       if (category === 川菜return typeIcons[辣椒];       if (category === 火锅return typeIcons[火锅];       if (category === 烧烤/烤肉return typeIcons[烤肉];       if (category === 烧烤return typeIcons[烧烤];       if (category === 日料return typeIcons[日料];       if (category === 韩餐return typeIcons[韩餐];       if (category === 西餐return typeIcons[牛排];       if (category === 面馆return typeIcons[面食];       if (category === 小吃return typeIcons[小吃];       if (category === 甜品/饮品return typeIcons[甜点];       if (category === 咖啡馆return typeIcons[咖啡];       if (category === 快餐return typeIcons[快餐];       if (category === 海鲜return typeIcons[海鲜];       return typeIcons[default];     }          // 初始化地图     constmap = new AMap.Map(map, {       center: [120.1550730.274085], // 杭州中心       zoom12     });     // 移除所有 data.forEach(...) 相关的marker渲染,只保留 renderMarkers() 控制     // ... existing code ...     // 图例渲染     const legend = document.getElementById(legend);     legend.innerHTML = legendTypes.map(t =>       `<div class="legend-item"><img class=legend-icon src=${typeIcons[t.type]} alt=${t.label} />${t.label}</div>`     ).join();     // InfoWindow全局变量     let infoWindow = null;     // label样式     const style = document.createElement(style);     style.innerHTML = `       .marker-label {         font-size: 16px;         color: #fff;         background: rgba(34,34,34,0.75);         border-radius: 6px;         padding: 2px 10px;         box-shadow: 02px 8px rgba(0,0,0,0.15);         white-space: nowrap;         transition: background 0.2s;         border: none;       }       .marker-label-hover {         background: rgba(34,34,34,0.95);         color: #ffe58f;         border: none;       }       /* 强制覆盖高德地图label的蓝色边框和背景 */       .amap-marker-label {         border: none !important;         box-shadow: none !important;         background: none !important;         padding: 0 !important;       }     `;     document.head.appendChild(style);     // 动态生成类别筛选项     const allCategories = Array.from(new Set(data.map(d => d.category).filter(Boolean)));     const filterCategoryList = document.getElementById(filter-category-list);     filterCategoryList.innerHTML = allCategories.map(cat =>       `<label><input type=checkbox name=category value=${cat}${cat}</label>`     ).join();     // 默认勾选5星     document.querySelector(input[name="recommend"][value="5"]).checked = true;     // 筛选逻辑     function getSelectedRecommends() {       return Array.from(document.querySelectorAll(input[name="recommend"]:checked)).map(i => i.value);     }     function getSelectedCategories(){       return Array.from(document.querySelectorAll(input[name="category"]:checked)).map(i => i.value);     }     let allMarkers = [];     function renderMarkers(){       // 清除旧marker       allMarkers.forEach(m => m.setMap(null));       allMarkers = [];       const selectedRecommends = getSelectedRecommends();       const selectedCategories = getSelectedCategories();       let filtered = data;       // 取交集策略:都选了才展示       if (selectedRecommends.length) {         filtered = filtered.filter(d => selectedRecommends.includes(d.recommend_index));       }       if (selectedCategories.length) {         filtered = filtered.filter(d => selectedCategories.includes(d.category));       }       // 如果都没选,展示全部       if (!selectedRecommends.length && !selectedCategories.length) {         filtered = data;       }       filtered.forEach(item => {         if (!item.location) return;         const [lng, lat] = item.location.split(,).map(Number);         const iconUrl = getCategoryIcon(item.category);         const marker = new AMap.Marker({           position: [lng, lat],           iconnew AMap.Icon({             image: iconUrl,             sizenew AMap.Size(3232),             imageSizenew AMap.Size(3232)           }),           title: item.name,           offsetnew AMap.Pixel(-16, -36),           label: {             content`<span class=marker-label>${item.name}</span>`,             directiontop,             offsetnew AMap.Pixel(0, -16),             clickabletrue           }         });         marker.on(mouseover, () => marker.setLabel({ ...marker.getLabel(), content`<span class=marker-label marker-label-hover>${item.name}</span>` }));         marker.on(mouseout, () => marker.setLabel({ ...marker.getLabel(), content`<span class=marker-label>${item.name}</span>` }));         marker.on(click, () => showInfo(item));         marker.on(labelClick, () => showInfo(item));         marker.setMap(map);         allMarkers.push(marker);       });     }     // 监听筛选项变化     document.querySelectorAll(.filter-panel input[type="checkbox"]).forEach(input => {       input.addEventListener(change, renderMarkers);     });     // 初始化时渲染一次     renderMarkers();     function showInfo(item){       // 关闭已有 infoWindow       if (infoWindow) {         infoWindow.close();       }       // 构建详情内容       const content = `         <div style="min-width:220px;max-width:320px;">           <div style="font-size:18px;font-weight:bold;margin-bottom:6px;">${item.name}</div>           <div style="margin-bottom:4px;">${item.address ?  + item.address : }</div>           ${item.photo ? `<img src="${item.photo}" style="width:100%;max-height:120px;object-fit:cover;border-radius:6px;margin-bottom:6px;">` : }           <div>类型:${item.category || item.type || }</div>           <div>推荐指数:${item.recommend_index || }</div>           <div>评分:${item.rating || }</div>           <div>人均:${item.cost ?  + item.cost : }</div>           <div>营业时间:${item.open_time || }</div>           <div>电话:${item.phone || }</div>           <div style="margin-top:4px;">${item.advantage || }</div>         </div>       `;       // 取坐标       const [lng, lat] = item.location.split(,).map(Number);       infoWindow = new AMap.InfoWindow({         content,         offsetnew AMap.Pixel(0, -36)       });       infoWindow.open(map, [lng, lat]);     }     // 点击地图空白处关闭详情弹窗     map.on(clickfunction() {       if (infoWindow) infoWindow.close();     });   </script> </body> </html>

“去哪吃”源码

<!DOCTYPE html> <html lang="zh-CN"> <head>   <meta charset="UTF-8">   <title>去哪吃-AI推荐餐厅</title>   <meta name="viewport" content="width=device-width, initial-scale=1.0">   <style>     htmlbody#map { height100%margin0padding0; }     .amap-info-content { font-size14px; }     .legend {       position: absolute;       top10px;       left10px;       backgroundrgba(255,255,255,0.9);       border-radius6px;       padding10px;       box-shadow02px 8px rgba(0,0,0,0.15);       z-index1000;     }     .legend-item { display: flex; align-items: center; margin-bottom6px; }     .legend-icon { width20pxheight20pxmargin-right6px; }     .marker-label {       font-size16px;       color#fff;       backgroundrgba(34,34,34,0.75);       border-radius6px;       padding2px 10px;       box-shadow02px 8px rgba(0,0,0,0.15);       white-space: nowrap;       transition: background 0.2s;       border: none;     }     .marker-label-hover {       backgroundrgba(34,34,34,0.95);       color#ffe58f;       border: none;     }     /* 强制覆盖高德地图label的蓝色边框和背景 */     .amap-marker-label {       border: none !important;       box-shadow: none !important;       background: none !important;       padding0 !important;     }     .filter-panel {       position: absolute;       top40px;       right20px;       width220px;       backgroundrgba(255,255,255,0.97);       border-radius10px;       box-shadow02px 16px rgba(0,0,0,0.13);       padding18px 18px 12px 18px;       z-index1200;       font-familyPingFang SCMicrosoft YaHei, Arial, sans-serif;       font-size15px;       color#222;       transition: box-shadow 0.2s;       max-height600px;       overflow-y: auto;     }     .filter-title {       font-size18px;       font-weight: bold;       margin-bottom12px;       letter-spacing2px;     }     .filter-section { margin-bottom14px; }     .filter-label { font-size14pxmargin-bottom6pxcolor#666; }     .filter-recommend label.filter-category label {       display: inline-block;       margin-right10px;       margin-bottom6px;       cursor: pointer;       font-size14px;     }     .filter-category {       max-height280px;       overflow-y: auto;       border-radius6px;       background#f7f7f7;       padding6px 4px 4px 8px;       border1px solid #eee;     }     .filter-category label { width120px; }     .filter-panel input[type="checkbox"] {       accent-color#1890ff;       margin-right3px;       vertical-align: middle;     }     .detail-panel {       position: absolute;       top40px;       right20px;       width400px;       backgroundrgba(255,255,255,0.98);       border-radius14px;       box-shadow04px 32px rgba(0,0,0,0.13);       padding28px 28px 18px 28px;       z-index1200;       font-familyPingFang SCMicrosoft YaHei, Arial, sans-serif;       font-size16px;       color#222;       max-height80vh;       overflow-y: auto;       transition: box-shadow 0.2s;       display: none;     }     .detail-title {       font-size22px;       font-weight: bold;       margin-bottom10px;       color#1890ff;       letter-spacing1px;     }     .detail-recommend {       background#fffbe6;       color#faad14;       border-radius8px;       padding8px 14px;       font-size18px;       font-weight: bold;       margin-bottom16px;       box-shadow02px 8px rgba(250,173,20,0.08);       display: flex;       align-items: center;       gap8px;     }     .detail-photo {       width100%;       max-height180px;       object-fit: cover;       border-radius8px;       margin-bottom10px;       box-shadow02px 8px rgba(0,0,0,0.08);     }     .detail-field { margin-bottom8px; }     .detail-label { color#888margin-right6px; }     .detail-analysis {       background#f6ffed;       color#389e0d;       border-radius8px;       padding10px 14px;       margin-top12px;       font-size15px;       box-shadow02px 8px rgba(56,158,13,0.06);     }     .detail-panel-title {       font-size20px;       font-weight: bold;       text-align: center;       margin-bottom18px;       color#222;       letter-spacing1px;     }   </style>   <!-- 高德地图 JS API -->   <script src="https://webapi.amap.com/maps?v=2.0&key=「你的高德 apikey」"></script> </head> <body>   <div id="map"></div>   <div id="detail-panel" class="detail-panel"></div>   <script>     // 直接嵌入餐厅数据     const data = [       {         "name""杭州市阿里巴巴滨江园区(出发点)",         "type""出发点",         "location""120.190371,30.189600",         "address""杭州市滨江区网商路6**号",         "photo""https://gw.alicdn.com/imgextra/i2/O1CN014DVQlb1oVqsnsrbiT_!!6000000005231-2-tps-64-64.png",         "cost""-",         "open_time""-",         "rating""-",         "phone""-",         "advantage""公司园区,交通便利,地铁直达,周边配套齐全。",         "recommend_index""-",         "category""出发点",         "is_origin"true       },       {         "name""海*捞火锅(中南乐游城店)",         "type""火锅",         "location""120.190309,30.193994",         "address""江南大道10**号",         "photo""https://aos-comment.amap.com/B0GU9MT4N7/comment/6845185a8eedb6301996b74240ba088b_2048_2048_80.jpg",         "cost""80.00",         "open_time""09:00-07:00",         "rating""4.7",         "phone""-",         "advantage""服务优质,环境舒适,适合聚餐。",         "recommend_index""5",         "category""火锅",         "distance_m"2030,         "duration_s"459,         "traffic""驾车约8分钟,公交便利,地铁近。",         "surrounding""中南乐游城、知嘛家等商场,生活便利。",         "analysis""距离适中,交通便利,配套丰富,适合团队聚餐。"       },       {         "name""黄*海鲜大排档(滨江店)",         "type""海鲜",         "location""120.195922,30.197039",         "address""浙商发展大厦*座1-2楼(**地铁站G口)",         "photo""https://aos-comment.amap.com/B0J1B5R0EY/comment/0e8ed310fa3bb3135ddbefe27244562f_2048_2048_80.jpg",         "cost""139.00",         "open_time""11:00-15:30 17:30-02:30",         "rating""4.7",         "phone""-",         "advantage""菜品新鲜,适合多人聚餐,夜宵丰富。",         "recommend_index""5",         "category""海鲜",         "distance_m"2395,         "duration_s"478,         "traffic""驾车约8分钟,地铁直达,公交便利。",         "surrounding""知嘛家等商场,夜生活丰富。",         "analysis""交通便利,夜宵丰富,适合下班聚会。"       },       {         "name""群*饭店(信诚路店)",         "type""中餐",         "location""120.174707,30.183901",         "address""滨安路11**号(近信诚路)",         "photo""http://store.is.autonavi.com/showpic/2ac9e95efffe6e27a8eda43ef8271134",         "cost""66.00",         "open_time""11:00-13:30 16:00-20:30",         "rating""4.7",         "phone""-",         "advantage""家常菜,性价比高,适合日常用餐。",         "recommend_index""4",         "category""中餐",         "distance_m"2797,         "duration_s"757,         "traffic""驾车约13分钟,公交可达。",         "surrounding""周边无大型商场,环境安静。",         "analysis""性价比高,适合小团队日常聚餐。"       },       {         "name""龙*·滨江烤全羊(杭州总店)",         "type""烤肉",         "location""120.195890,30.184231",         "address""长河路47*号和瑞科技广场S*幢楼下",         "photo""http://store.is.autonavi.com/showpic/45d72ae00c69cf56d4fe514b1346c55c",         "cost""132.00",         "open_time""11:00-21:00",         "rating""4.8",         "phone""-",         "advantage""烤全羊特色,适合聚会,环境宽敞。",         "recommend_index""5",         "category""烤肉",         "distance_m"1226,         "duration_s"254,         "traffic""驾车约4分钟,公交可达。",         "surrounding""保利·时光里等商场,配套齐全。",         "analysis""距离最近,评分最高,配套好,强烈推荐。"       },       {         "name""东*方(杭州保利时光里店)",         "type""中餐",         "location""120.197088,30.182094",         "address""长河街道长河路与滨康路交叉口保利时光里*楼13号楼2017商铺",         "photo""http://store.is.autonavi.com/query_pic?id=st1ef630c8-39ef-4d63-93ce-ba3a9732851e&user=search&operate=original",         "cost""-",         "open_time""-",         "rating""4.8",         "phone""-",         "advantage""环境优雅,适合家庭聚餐。",         "recommend_index""5",         "category""中餐",         "distance_m"2088,         "duration_s"491,         "traffic""驾车约8分钟,公交便利。",         "surrounding""保利·时光里等商场,生活便利。",         "analysis""评分高,环境好,适合家庭和小型聚会。"       }     ];     // 图标映射表     const typeIcons = {       辣椒「需要自己准备」,       甜点「需要自己准备」,       咖啡「需要自己准备」,       中餐「需要自己准备」,       日料「需要自己准备」,       快餐「需要自己准备」,       烤肉「需要自己准备」,       烧烤「需要自己准备」,       星星「需要自己准备」,       火锅「需要自己准备」,       牛排「需要自己准备」,       面食「需要自己准备」,       海鲜「需要自己准备」,       韩餐「需要自己准备」,       小吃「需要自己准备」,       粤菜「需要自己准备」,       default「需要自己准备」     };     function getCategoryIcon(category){       if (!category) return typeIcons[default];       if (category === 出发点return typeIcons[星星];       if (category === 中餐 || category === 杭帮菜/浙江菜 || category === 湘菜 || category === 东北菜 || category === 素食 || category === 私房菜return typeIcons[中餐];       if (category === 粤菜return typeIcons[粤菜];       if (category === 川菜return typeIcons[辣椒];       if (category === 火锅return typeIcons[火锅];       if (category === 烧烤/烤肉 || category === 烤肉return typeIcons[烤肉];       if (category === 烧烤return typeIcons[烧烤];       if (category === 日料return typeIcons[日料];       if (category === 韩餐return typeIcons[韩餐];       if (category === 西餐return typeIcons[牛排];       if (category === 面馆return typeIcons[面食];       if (category === 小吃return typeIcons[小吃];       if (category === 甜品/饮品return typeIcons[甜点];       if (category === 咖啡馆return typeIcons[咖啡];       if (category === 快餐return typeIcons[快餐];       if (category === 海鲜return typeIcons[海鲜];       return typeIcons[default];     }     // 初始化地图     constmap = new AMap.Map(map, {       center: [120.19037130.189600],       zoom14     });     let allMarkers = [];     let allLines = [];     function renderMarkers(){       // 清除旧marker和线       allMarkers.forEach(m => m.setMap(null));       allMarkers = [];       allLines.forEach(l => l.setMap(null));       allLines = [];       if (!data.length) return;       // 出发点       const origin = data.find(d => d.is_origin);       // 餐厅       const restaurants = data.filter(d => !d.is_origin);       // 出发点marker       if (origin) {         const [lng, lat] = origin.location.split(,).map(Number);         const marker = new AMap.Marker({           position: [lng, lat],           iconnew AMap.Icon({             image: getCategoryIcon(出发点),             sizenew AMap.Size(4040),             imageSizenew AMap.Size(4040)           }),           title: origin.name,           offsetnew AMap.Pixel(-20, -40),           label: {             content`<span class=marker-label>${origin.name}</span>`,             directiontop,             offsetnew AMap.Pixel(0, -20),             clickabletrue           }         });         marker.setMap(map);         allMarkers.push(marker);       }       // 餐厅marker和连线       restaurants.forEach(item => {         if (!item.location) return;         const [lng, lat] = item.location.split(,).map(Number);         const iconUrl = getCategoryIcon(item.category);         const marker = new AMap.Marker({           position: [lng, lat],           iconnew AMap.Icon({             image: iconUrl,             sizenew AMap.Size(3232),             imageSizenew AMap.Size(3232)           }),           title: item.name,           offsetnew AMap.Pixel(-16, -36),           label: {             content`<span class=marker-label>${item.name}</span>`,             directiontop,             offsetnew AMap.Pixel(0, -16),             clickabletrue           }         });         marker.on(mouseover, () => marker.setLabel({ ...marker.getLabel(), content`<span class=marker-label marker-label-hover>${item.name}</span>` }));         marker.on(mouseout, () => marker.setLabel({ ...marker.getLabel(), content`<span class=marker-label>${item.name}</span>` }));         marker.on(click, () => showInfo(item));         marker.on(labelClick, () => showInfo(item));         marker.setMap(map);         allMarkers.push(marker);         // 连线         if (origin && origin.location) {           const [olng, olat] = origin.location.split(,).map(Number);           const line = new AMap.Polyline({             path: [ [olng, olat], [lng, lat] ],             strokeColor#1890ff,             strokeWeight4,             strokeOpacity0.7,             isOutlinetrue,             outlineColor#fff           });           line.setMap(map);           allLines.push(line);         }       });     }     // 智能推荐(评分最高且距离最近)     function showRecommend(){       if (!data.length) return;       const restaurants = data.filter(d => !d.is_origin);       // 评分优先,距离次之       let best = restaurants[0];       restaurants.forEach(r => {         if (           Number(r.rating) > Number(best.rating) ||           (r.rating === best.rating && r.distance_m < best.distance_m)         ) {           best = r;         }       });       showInfo(best, true);     }     // 右侧详情面板     function showInfo(item, isRecommend = false){       const panel = document.getElementById(detail-panel);       if (!item) { panel.style.display = nonereturn; }       let html = ;       html += `<div class=detail-panel-title>AI推荐餐厅</div>`;       if (isRecommend) {         html += `<div class=detail-recommend> 智能推荐:<span>${item.name}</span></div>`;       }       html += `<div class=detail-title>${item.name}</div>`;       if (item.photo) html += `<img class=detail-photo src=${item.photo} alt=${item.name}>`;       html += `<div class=detail-field><span class=detail-label>类型:</span>${item.type || }</div>`;       html += `<div class=detail-field><span class=detail-label>地址:</span>${item.address || }</div>`;       if (item.distance_m) html += `<div class=detail-field><span class=detail-label>距离:</span>${(item.distance_m/1000).toFixed(2)} km</div>`;       if (item.duration_s) html += `<div class=detail-field><span class=detail-label>车程:</span>${Math.round(item.duration_s/60)} 分钟</div>`;       if (item.cost && item.cost !== -) html += `<div class=detail-field><span class=detail-label>人均:</span>¥${item.cost}</div>`;       if (item.open_time && item.open_time !== -) html += `<div class=detail-field><span class=detail-label>营业时间:</span>${item.open_time}</div>`;       if (item.rating && item.rating !== -) html += `<div class=detail-field><span class=detail-label>评分:</span>${item.rating}</div>`;       if (item.phone && item.phone !== -) html += `<div class=detail-field><span class=detail-label>电话:</span>${item.phone}</div>`;       if (item.traffic) html += `<div class=detail-field><span class=detail-label>交通:</span>${item.traffic}</div>`;       if (item.surrounding) html += `<div class=detail-field><span class=detail-label>周边配套:</span>${item.surrounding}</div>`;       if (item.advantage) html += `<div class=detail-field><span class=detail-label>优点:</span>${item.advantage}</div>`;       if (item.analysis) html += `<div class=detail-analysis>${item.analysis}</div>`;       panel.innerHTML = html;       panel.style.display = block;     }     // 地图点击空白关闭详情     map.on(clickfunction() {       document.getElementById(detail-panel).style.display = none;     });     renderMarkers();     showRecommend();   </script> </body> </html>

  以上就是小编为大家整理的用 AI + 高德地图 MCP,3 小时做出杭州美食地图,想要了解更多优质的相关资讯,请大家多多关注"大世界日记"。

最新推荐
猜你喜欢