高德地图 JS-SDK 实现教程
高德地图 JS-SDK 实现教程:定位、地图选点、地址解析等
适用地点选择、地址显示、表单填写等场景,全面支持移动端、手机浏览器和 PC端环境
一、创建应用&Key
前端(JS-SDK、地图组件)
- 登陆 高德开放平台
- 创建应用,示例名:
MapSelectorApp - 添加 Key:选择“Web端(JS API)”
- 配置域名白名单(如
yourdomain.com、*.yourdomain.com)
后端(Web服务 API)
- 方便在后端调用
https://restapi.amap.com/v3/geocode/regeo - 选择服务平台为 Web服务
- 推荐单独创建一个 Web 服务 Key,与前端分离管理
- 配置 IP 白名单
二、前端 HTML 实现
环境依赖
<script src="https://webapi.amap.com/maps?v=2.0&key=替换为你的高德地图 JSAPI KEY"></script>
基础组件
<div id="controls"><button onclick="locateUser()">📍 定位当前位置</button><button onclick="confirmSelection()">✅ 确认选点</button><div id="address">地址信息:-</div>
</div>
<div id="container"></div>
核心 JS 逻辑 (amap.js)
let map, marker, selectedLngLat;
window.onload = function () {map = new AMap.Map("container", { resizeEnable: true, zoom: 14 });locateUser();map.on("click", e => {selectedLngLat = e.lnglat;addMarker(e.lnglat);reverseGeocode(e.lnglat);});
};function addMarker(pos) {if (!marker) marker = new AMap.Marker({ position: pos, map });else marker.setPosition(pos);
}function locateUser() {AMap.plugin('AMap.Geolocation', function () {const geo = new AMap.Geolocation({ enableHighAccuracy: true, timeout: 10000 });map.addControl(geo);geo.getCurrentPosition((status, result) => {if (status === 'complete') {const pos = result.position;map.setCenter(pos);addMarker(pos);selectedLngLat = pos;reverseGeocode(pos);} else {alert("定位失败:" + result.message);}});});
}function reverseGeocode(lnglat) {fetch(`/amap/reverse-geocode?lng=${lnglat.lng}&lat=${lnglat.lat}`).then(res => res.json()).then(data => {document.getElementById("address").innerText = data.address ? `地址信息:${data.address}` : `⚠️ 地址解析失败`}).catch(err => {console.error(err);document.getElementById("address").innerText = "⚠️ 网络或服务器错误";});
}function confirmSelection() {if (!selectedLngLat) return alert("请选择地点");const text = document.getElementById("address").innerText;alert(`✅ 选点结果\n经纬度: ${selectedLngLat.lng}, ${selectedLngLat.lat}\n${text}`);
}
三、Spring Boot 后端 API
接口 URL
GET /amap/reverse-geocode?lng=113.83&lat=22.79
管理 Controller
@RestController
@RequestMapping("/amap")
public class AmapController {@Autowired private AmapService amapService;@GetMapping("/reverse-geocode")public ResponseEntity<?> reverseGeocode(@RequestParam("lng") double lng, @RequestParam("lat") double lat) {try {String address = amapService.getAddressFromCoordinates(lng, lat);return ResponseEntity.ok(Map.of("address", address));} catch (Exception e) {return ResponseEntity.status(500).body("\u5730\u5740\u89e3\u6790\u5931\u8d25: " + e.getMessage());}}
}
地址解析 Service
@Service
public class AmapService {private static final String AMAP_KEY = "替换你的Web服务Key";private static final String GEOCODE_URL = "https://restapi.amap.com/v3/geocode/regeo";public String getAddressFromCoordinates(double lng, double lat) {UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(GEOCODE_URL).queryParam("key", AMAP_KEY).queryParam("location", lng + "," + lat).queryParam("output", "json");RestTemplate restTemplate = new RestTemplate();Map<String, Object> response = restTemplate.getForObject(builder.toUriString(), Map.class);if (response == null || !"1".equals(response.get("status"))) {throw new RuntimeException("\u5730\u5740\u89e3\u6790API\u5931\u8d25:" + response);}Map<String, Object> regeocode = (Map<String, Object>) response.get("regeocode");if (regeocode == null || regeocode.get("formatted_address") == null) {throw new RuntimeException("\u65e0\u6709\u6548\u5730\u5740");}return (String) regeocode.get("formatted_address");}
}
四、配置 WireGuard 分流网络
目标:仅展前后端 API 请求进入高德手机服务器
WireGuard 配置示例
[Peer]
AllowedIPs = 120.77.134.0/24, 2408:4003::/32
Endpoint = [你的防火墙服务器]:51820
可用 nslookup restapi.amap.com 查看实际服务器 IP
五、如何获取域名的 IP 地址
为了精准设置 WireGuard 的路由规则,我们需要获取目标域名的实际 IP。
✅ 方法一:命令行使用 nslookup
nslookup restapi.amap.com
返回示例:
服务器: dns.google
Address: 8.8.8.8非权威应答:
名称: restapi.amap.com.gds.alibabadns.com
Addresses: 2408:4003:1f10::2b42408:4003:1f40::2e5120.77.134.57
Aliases: restapi.amap.com
✅ 方法二:使用 ping
ping restapi.amap.com
输出结果将包含类似:
正在 Ping restapi.amap.com.gds.alibabadns.com [120.77.134.169] 具有 32 字节的数据:
来自 120.77.134.169 的回复: 字节=32 时间=59ms TTL=95
来自 120.77.134.169 的回复: 字节=32 时间=70ms TTL=95
来自 120.77.134.169 的回复: 字节=32 时间=76ms TTL=95
来自 120.77.134.169 的回复: 字节=32 时间=58ms TTL=95120.77.134.169 的 Ping 统计信息:数据包: 已发送 = 4,已接收 = 4,丢失 = 0 (0% 丢失),
往返行程的估计时间(以毫秒为单位):最短 = 58ms,最长 = 76ms,平均 = 65ms
✅ 方法三:使用在线工具查询 IP
- https://tool.chinaz.com/dns
- https://dnschecker.org
- https://ip138.com
✅ 如何使用这些 IP
将得到的 IPv4 地址(如 120.77.134.57)用于你的代理配置中:
AllowedIPs = 120.77.134.57/32
结论
| 组件 | Key类型 | 权限 | 简述 |
|---|---|---|---|
| 前端 JS SDK | Web端 JS API | 需配置域名 | 显示地图,选点,定位 |
| 后端 API | Web 服务 Key | 需配置 IP | 进行地址解析 |
附录:完整文件(可自行补全代码)
pom.xml ✅
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>org.example</groupId><artifactId>DemoAPI</artifactId><version>1.0-SNAPSHOT</version><packaging>jar</packaging><!-- Spring Boot 父项目 --><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>3.2.5</version><relativePath/></parent><properties><maven.compiler.source>17</maven.compiler.source><maven.compiler.target>17</maven.compiler.target><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding></properties><dependencies><!-- Spring Boot Web 模块(包含内嵌 Tomcat) --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!-- 开发工具(自动重启,非必须) --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-devtools</artifactId><scope>runtime</scope></dependency></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></build>
</project>
amap.html ✅
<!DOCTYPE html>
<html lang="zh">
<head><meta charset="UTF-8" /><title>地图选点</title><style>html, body, #container {height: 100%; width: 100%; margin: 0; padding: 0;}#controls {position: absolute;top: 10px;left: 10px;z-index: 999;background: rgba(255, 255, 255, 0.9);padding: 8px 12px;border-radius: 6px;box-shadow: 0 0 5px #ccc;}#controls button {margin-right: 10px;padding: 6px 12px;font-size: 14px;cursor: pointer;}</style><!-- 替换为你的高德地图 JSAPI KEY --><script src="https://webapi.amap.com/maps?v=2.0&key=替换为你的高德地图 JSAPI KEY"></script>
</head>
<body>
<div id="controls"><button onclick="locateUser()">📍 定位当前位置</button><button onclick="confirmSelection()">✅ 确认选点</button><div id="address">地址信息:-</div>
</div>
<div id="container"></div><!-- 引入地图逻辑 JS -->
<script src="../js/amap.js"></script></body>
</html>
amap.js ✅
let map;
let marker = null;
let selectedLngLat = null;window.onload = function () {map = new AMap.Map("container", {resizeEnable: true,zoom: 14});// 自动定位locateUser();// 地图点击选点map.on("click", function (e) {const lnglat = e.lnglat;addMarker(lnglat);selectedLngLat = lnglat;reverseGeocode(lnglat);});
};function addMarker(lnglat) {if (!marker) {marker = new AMap.Marker({position: lnglat,map: map});} else {marker.setPosition(lnglat);}
}function locateUser() {AMap.plugin('AMap.Geolocation', function () {const geo = new AMap.Geolocation({enableHighAccuracy: true,timeout: 10000,showButton: false});map.addControl(geo);geo.getCurrentPosition(function (status, result) {if (status === 'complete') {const position = result.position;map.setCenter(position);addMarker(position);selectedLngLat = position;reverseGeocode(position);} else {alert("定位失败:" + result.message);}});});
}function reverseGeocode(lnglat) {fetch(`/amap/reverse-geocode?lng=${lnglat.lng}&lat=${lnglat.lat}`).then(res => res.json()).then(data => {if (data.address) {document.getElementById("address").innerText = "地址信息:" + data.address;} else {document.getElementById("address").innerText = "⚠️ 地址解析失败,仅返回坐标";}}).catch(err => {console.error(err);document.getElementById("address").innerText = "⚠️ 网络或服务器错误";});
}function confirmSelection() {if (!selectedLngLat) {alert("请先在地图上点击选点或使用定位");return;}const text = document.getElementById("address").innerText;alert("✅ 选点结果:\n经纬度:" + selectedLngLat.lng + ", " + selectedLngLat.lat + "\n" + text);
}
AmapService ✅
package org.example.service;import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.UriComponentsBuilder;import java.util.HashMap;
import java.util.Map;/*** ==================================================* This class AmapService is responsible for [高德地图服务类].** @author Darker* @version 1.0* ==================================================*/@Service
public class AmapService {private static final String AMAP_KEY = "替换你的高德地图的 WEB KEY";private static final String GEOCODE_URL = "https://restapi.amap.com/v3/geocode/regeo";public String getAddressFromCoordinates(double lng, double lat) {RestTemplate restTemplate = new RestTemplate();UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(GEOCODE_URL).queryParam("key", AMAP_KEY).queryParam("location", lng + "," + lat).queryParam("output", "json");Map<String, Object> response = restTemplate.getForObject(builder.toUriString(), Map.class);if (response == null || !"1".equals(response.get("status"))) {throw new RuntimeException("高德地址解析失败,返回状态:" + response);}// 解析 formatted_addressMap<String, Object> regeocode = (Map<String, Object>) response.get("regeocode");if (regeocode == null || regeocode.get("formatted_address") == null) {throw new RuntimeException("未能获取到地址信息");}return (String) regeocode.get("formatted_address");}
}
AmapController ✅
package org.example.controller;import org.example.service.AmapService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;import java.util.Collections;/*** ==================================================* This class AmapController is responsible for [功能描述].** @author Darker* @version 1.0* ==================================================*/@RestController
@RequestMapping("/amap")
public class AmapController {@Autowiredprivate AmapService amapService;@GetMapping("/reverse-geocode")public ResponseEntity<?> reverseGeocode(@RequestParam("lng") double lng, @RequestParam("lat") double lat) {try {String address = amapService.getAddressFromCoordinates(lng, lat);return ResponseEntity.ok(Collections.singletonMap("address", address));} catch (Exception e) {return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("地址解析失败: " + e.getMessage());}}
}
相关文章:
高德地图 JS-SDK 实现教程
高德地图 JS-SDK 实现教程:定位、地图选点、地址解析等 适用地点选择、地址显示、表单填写等场景,全面支持移动端、手机浏览器和 PC端环境 一、创建应用&Key 前端(JS-SDK、地图组件) 登陆 高德开放平台创建应用,…...
07软件测试需求分析案例-修改用户信息
修改用户信息是后台管理菜单的一个功能模块,只有admin才有修改权限。包括查询用户名进行显示用户相关信息,并且修改用户相关信息的功能。 1.1 通读文档 通读需求规格说明书是提取信息,提出问题,输出具有逻辑、规则、流程的业务…...
分层对象模型:PO、DTO、VO、BO定义区别与使用场景
目录 前言 PO(持久化对象) DTO(数据传输对象) VO(视图对象) BO(业务对象) 关键区别总结 典型应用场景 为什么要分层设计 工具支持 前言 在开发中,我们经常遇到…...
设计模式 --- 状态模式
状态模式是一种行为型设计模式,允许对象在内部状态改变时动态改变其行为,使对象的行为看起来像是改变了。该模式通过将状态逻辑拆分为独立类,消除复杂的条件分支语句,提升代码的可维护性和扩展性。 状态模式的…...
Java多态课堂练习题
Java多态课堂练习题 题目:动物乐园的多态展示 背景设定: 设计一个动物乐园程序,展示不同类型动物的行为特点,要求使用多态特性实现。 1. 基础类设计(已给出部分代码) // 基类:动物 abstract…...
SAP系统中的借货
问题:什么是借贷? 解答:记账符号反映的是各种经济业务数量的增加和减少。 二:怎么区分借贷增减? 解答:“借”和“贷”何时为增加、何时为减少,必须结合账户的具体性质才能准确说明…...
深入剖析Go Channel:从底层原理到高阶避坑指南|Go语言进阶(5)
文章目录 引言channel的底层数据结构channel操作原理发送操作(ch <- data)接收操作(<-ch) 常见陷阱及避坑指南1. 死锁问题2. 关闭channel的错误方式3. 内存泄漏4. nil channel特性5. 性能考量 最佳实践总结 引言 Channel是Go语言实现CSP并发模型的核心机制,提…...
OpenCV 图形API(31)图像滤波-----3x3 腐蚀操作函数erode3x3()
操作系统:ubuntu22.04 OpenCV版本:OpenCV4.9 IDE:Visual Studio Code 编程语言:C11 算法描述 使用3x3矩形结构元素腐蚀图像。 该函数通过使用中心作为锚点的3x3矩形结构元素来腐蚀源图像。腐蚀操作可以应用多次(迭代࿰…...
5.5 GitHub数据秒级分析核心揭秘:三层提示工程架构设计解析
GitHub Sentinel Agent 分析报告功能设计与实现 关键词:GitHub 数据分析, 提示工程设计, Pull Request 分析, Issues 跟踪, 竞品对比 项目进展报告生成功能设计 报告生成模块是 GitHub Sentinel 的核心功能,通过三层嵌套式提示工程架构实现深度分析: #mermaid-svg-vdHRUan…...
【Java学习笔记】Java初级阶段代码规范
Java 初级阶段代码规范 1. 类、方法的注释,要以 javadoc 的方式来写。 2. 非 Java Doc 的注释,往往是给代码的维护者看的,着重告读者为什么这样写,如何修改,注重什么问题等 3. 使用 tab 操作,实现缩进&am…...
AI Agent开发大全第二十八课-MCP实现本地命令调用怎么做的?
开篇 MCP很强大,Client端一旦实现了稳定的连接和执行流程后任Server端随意改动都可兼容,这就是热插拨功能。 如果我们仅仅满足于MCP查点网上资料、读点图片即文字型的功能肯定是不能充分发挥MCP的强大之处的,正应了Google以及Anthropic最近的研究报告上说的:不要再在chat…...
macOS 上使用 Homebrew 安装和配置 frp 客户端
macOS 上使用 Homebrew 安装和配置 frp 客户端 (frpc) 指南 frp (Fast Reverse Proxy) 是一款高性能的反向代理应用,常用于内网穿透。本文将介绍在 macOS 上使用 Homebrew 安装 frpc,并进行配置和管理。 一、安装 frpc 使用 Homebrew 安装(…...
为 docker 拉取镜像配置代理
为 Docker 配置代理,有 两个层面 的操作:(1) Docker 守护进程(用于拉取镜像等操作),(2) Docker 容器内部(容器内应用的网络流量)。 我们这篇文章着重于前者,以下是详细步骤ÿ…...
A2A协议实现详解及示例
A2A协议概述 A2A (Agent2Agent) 是Google推出的一个开放协议,旨在使AI智能体能够安全地相互通信和协作。该协议打破了孤立智能体系统之间的壁垒,实现了复杂的跨应用自动化。[1] A2A协议的核心目标是让不同的AI代理能够相互通信、安全地交换信息以及在各…...
TDengine 语言连接器(Node.js)
简介 tdengine/websocket 是 TDengine 的官方 Node.js 语言连接器。Node.js 开发人员可以通过它开发存取 TDengine 数据库的应用软件。 Node.js 连接器源码托管在 GitHub。 Node.js 版本兼容性 支持 Node.js 14 及以上版本。 支持的平台 支持所有能运行 Node.js 的平台。 …...
ESP-IDF 开发框架:探索ESP32的强大底层世界
文章总结(帮你们节约时间) 介绍了ESP-IDF作为乐鑫ESP32系列芯片的官方开发框架。对比了Arduino、MicroPython和ESP-IDF三种ESP32开发方式的优缺点。详细讲解了ESP-IDF的核心特性和多层架构设计。展示了ESP-IDF的开发流程和应用场景。 你是否曾经好奇过…...
活动图与流程图的区别与联系:深入理解两种建模工具
目录 前言1. 活动图概述1.1 活动图的定义1.2 活动图的基本构成要素1.3 活动图的应用场景 2. 流程图概述2.1 流程图的定义2.2 流程图的基本构成要素2.3 流程图的应用场景 3. 活动图与流程图的联系4. 活动图与流程图的区别4.1 所属体系不同4.2 表达能力差异4.3 使用目的与语境4.4…...
如何在 Java 中对 PDF 文件进行数字签名(教程)
Java 本身并不原生支持 PDF 文件,因此若要对 PDF 进行数字签名,您需要使用一些专用的软件。本教程将演示如何使用 JPedal PDF 库来对 PDF 文件进行数字签名。 步骤: • 下载 JPedal 并将 Jar 文件添加到项目中 • 创建一个 PKCS#12 密…...
图片文本识别OCR+DeepSeekapi实现提取图片关键信息
用到的技术: 通过腾讯OCR文字识别,deepseek的api实现 目录 需求分析: 文字识别(OCR)具体实现步骤 起步工作 代码编写 deepseek整合消息,返回文本关键信息 起步工作 编写工具类 具体调用实现 具体…...
go 通过汇编分析函数传参与返回值机制
文章目录 概要一、前置知识二、汇编分析2.1、示例2.2、汇编2.2.1、 寄存器传值的汇编2.2.2、 栈内存传值的汇编 三、拓展3.1 了解go中的Duff’s Device3.2 go tool compile3.2 call 0x46dc70 & call 0x46dfda 概要 在上一篇文章中,我们研究了go函数调用时的栈布…...
解决Ubuntu Desktop 24.04 VMware中安装后不能全屏显示,只能居中的问题
Ubuntu Desktop 24.04 VMware中安装后不能全屏显示,只能居中。 sudo apt-get install open-vm-tools sudo apt-get install open-vm*...
【笔记ing】AI大模型-04逻辑回归模型
一个神经网络结构,其中的一个神经网络层,本质就是一个逻辑回归模型 深度神经网络的本质就是多层逻辑回归模型互相连接或采用一定的特殊连接的方式连接在一起构成的。其中每一个层本质就是一个逻辑回归模型。 逻辑回归模型基本原理 逻辑回归࿰…...
分布式ID生成方案的深度解析与Java实现
在分布式系统中,生成全局唯一的ID是一项核心需求,广泛应用于订单编号、用户信息、日志追踪等场景。分布式ID不仅需要保证全局唯一性,还要满足高性能、高可用性以及一定的可读性要求。本文将深入探讨分布式ID的概念、设计要点、常见生成方案&a…...
AF3 ProteinDataModule类解读
AlphaFold3 protein_datamodule 模块 ProteinDataModule 类继承自 PyTorch Lightning 数据模块(LightningDataModule),负责 ProteinFlow 数据的准备、加载、拆分、变换等逻辑封装在一起,便于训练过程中的统一管理和复现。 这个类承担了 AlphaFold3 训练和评估过程中的 数据…...
如何将一个8s的接口优化到500ms以下
最近换了个工作,刚入职就接了个活--优化公司自营app的接口性能,提升用户体验。 刚开始还以为是1s优化到500ms这种,或者500ms优化到200ms的接口,感觉还挺有挑战的。下好app体验了一下。好家伙,那个慢已经超过了我的忍耐…...
记录学习的第二十五天
今天终于又开始更新了。实在是星期六的蓝桥杯给了我一个大大的打击,今天终于好不容易缓过来了,可以好好学算法了。 还是老规划,力扣的每日一题。不过今天的每日一题我之前做过了,就又提交了一次来签到。 之后三道哈希表题目。 我一…...
linux电源管理(二),内核的CPUFreq(DVFS)和ARM的SCPI
更多linux系统电源管理相关的内容请看:https://blog.csdn.net/u010936265/article/details/146436725?spm1011.2415.3001.5331 1 简介 CPUFreq子系统位于drivers/cpufreq目录下,负责进行运行过程中CPU频率和电压的动态调整,即DVFS (Dynami…...
【LeetCode 热题 100】哈希 系列
📁1. 两数之和 本题就是将通过两层遍历优化而成的,为什么需要两层遍历,因为遍历 i 位置时,不知道i-1之前的元素是多少,如果我们知道了,就可以通过两数相加和target比较即可。 因为本题要求返回下标…...
ES6学习04-数组扩展:扩展运算符、新增方法
一、扩展运算符 1. 2. eg: 3. 二、新增方法 1. arguments 元素组合 类似数组对象 2....
Redis存储“大数据对象”的常用策略及StackOverflowError错误解决方案
Hi,大家好,我是灰小猿! 在一些功能的开发中,我们一般会有一些场景需要将得到的数据先暂时的存储起来,以便后面的接口或业务使用,这种场景我们一般常用的场景就是将数据暂时存储在缓存中,之后再…...
