# amap-demo
**Repository Path**: eightFlying/amap-demo
## Basic Information
- **Project Name**: amap-demo
- **Description**: 高德地图JS API使用技巧
- **Primary Language**: Unknown
- **License**: Not specified
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 1
- **Forks**: 0
- **Created**: 2020-10-21
- **Last Updated**: 2022-12-08
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
# 高德地图JS API使用技巧
> 本文提供了高德地图JS API的使用方法和一些常用的使用技巧。
> 高德地图的api已经非常成熟了,基本能满足大部分项目的需求,不过要注意的是部分高德官方提供的api有调用次数限制,生产环境使用时需要慎重。
## 演示项目
- 项目地址:[amap-demo](https://gitee.com/eightFlying/amap-demo.git)
- 版本:`JS API v1.4.15`
## 版本说明
> 之前一直使用的是v1.0+版本(v1.4.15),今年(2020)新发布了v2.0版本,说一说项目中使用两个版本遇到的问题
> 官方提供的 v1.0+ 升级 v2.0 的指南:[升级指南](https://lbs.amap.com/api/jsapi-v2/update),实际使用后发现有很多细节好像官方并未做说明(没找到)
#### v1.4.15 版本
- [v1.4 api 文档](https://lbs.amap.com/api/javascript-api/summary)
- 让 __地图旋转__ 某个角度,发现地图上原本自带的文字标注也跟着旋转了,既不美观又不方便阅读,v2.0版本文字标注会始终保持初始方向,不会旋转
- __地图缩放__ zoom只支持整数值,使得想将地图缩放至一个合适的大小很难,v2.0版本缩放zoom支持浮点数,可以更加精细的控制缩放
#### v2.0 版本
- [v2.0 api 文档](https://lbs.amap.com/api/jsapi-v2/documentation)
- 2.0 版本无法通过直接打开HTML文件的方式使用

需要启动一个静态服务,在服务中打开

- 需要浏览器开启硬件加速(一般都是默认开启),否则各图形渲染会出错,并且会变得非常卡顿,自定义地图样式也无法正常显示
- `Polyline` 和 `Polygon` 做出的矢量图形颜色变淡了,对比后发现 `Polyline` 的 `strokeOpacity` 属性默认值由 __`0.9 -> 0.5`__、`Polygon` 的 `fillOpacity` 属性默认值由 __`0.9 -> 0.5`__
- 各种覆盖物的层级zIndex与v1.0不一样了需要注意
- `Marker` 覆盖物的offset属性默认值由 `Pixel(-10,-34)` 变成了 `[0,0]`,其他覆盖物都有同样的问题
- 地图有默认的灰白色背景
- 谷歌浏览器中测试发现点击覆盖物显示信息窗体时,地图中所有的覆盖物都会抖一下,原因不详
## 基本用法
> [官方使用指引](https://lbs.amap.com/api/javascript-api/guide/abc/prepare)
#### 注册为高德开发者并引入脚本
1. 注册高德开发者账号之后申请 key (如:79bd92e2914923f528474aeb8d098209)
2. 直接通过 `script` 标签加载 地图JS API 脚本
```HTML
高德地图基本用法
```
3. 通过 `npm` 加载 jsapi loader(比较适合在vue或者react等项目中使用)
```javascript
import AMapLoader from '@amap/amap-jsapi-loader';
AMapLoader.load({
"key": "79bd92e2914923f528474aeb8d098209", // 申请好的Web端开发者Key,首次调用 load 时必填
"version": "1.4.15", // 指定要加载的 JSAPI 的版本,缺省时默认为 1.4.15
"plugins": [] // 需要使用的的插件列表,如比例尺'AMap.Scale'等
"AMapUI": { // 是否加载 AMapUI,缺省不加载
"version": '1.1', // AMapUI 缺省 1.1
"plugins":[], // 需要加载的 AMapUI ui插件
},
"Loca":{ // 是否加载 Loca, 缺省不加载
"version": '1.3.2' // Loca 版本,缺省 1.3.2
},
}).then((AMap)=>{
new AMap.Map('container');
}).catch(e => {
console.log(e);
})
```
#### 自定义地图样式
- 使用方法:设置 `mapStyle`
```javascript
let map = new AMap.Map('container',{
mapStyle: 'amap://styles/whitesmoke', //设置地图的显示样式
})
// 或者地图初始化之后再设置
map.setMapStyle('amap://styles/whitesmoke')
```
- 使用官方提供的地图样式

- 自定义地图样式,进入 [自定义地图平台](https://lbs.amap.com/dev/mapstyle/index) 定制地图风格,

- 共享地图样式,将地图样式复制到自己的帐号下,点击使用和分享,复制共享地图样式链接在浏览器中访问即可复制到自己账号进行二次编辑。


## 常用技巧
#### 隐藏左下角高德地图图标和文字
- 在项目中加入如下样式进行覆盖即可
- 注意 `高德地图v2.0` 中,当自定义地图样式中设置了带透明度的颜色时,需要去掉背景颜色以保证显示效果
```css
/* 隐藏高德logo */
.amap-logo{
opacity: 0 !important;
}
/* 隐藏高德版权信息 */
.amap-copyright{
opacity: 0 !important;
}
/* 隐藏v2.0高德背景颜色 */
.amap-container{
background: none !important;
}
```
#### 增大缩放级别
- 默认缩放级别为 `[3, 18]`,可以通过设置 `expandZoomRange` 和 `zooms` 将其扩大到 `[3, 20]`
```javascript
let map = new AMap.Map('container', {
mapStyle: 'amap://styles/f7623cef5816abe36ab37f641cd44f73', // 自定义地图样式
expandZoomRange: true, // 是否支持可以扩展最大缩放级别,和zooms属性配合使用
zooms: [3, 20], // 设置缩放级别范围 3-20 级
})
```
#### 限制地图范围
- 设置Map的限制区域,设定区域限制后,传入参数为限制的Bounds。地图仅在区域内可拖拽
```javascript
// 实例化一个地图对象,放入id为container的容器中
let map = new AMap.Map('container', {
mapStyle: 'amap://styles/f7623cef5816abe36ab37f641cd44f73', // 自定义地图样式
zoom: 12,
expandZoomRange: true, // 是否支持可以扩展最大缩放级别,和zooms属性配合使用
zooms: [3, 20], // 设置缩放级别范围 3-20 级
jogEnable: false, //是否使用缓动效果,关闭平移惯性感觉舒服一些
})
// 限制地图区域为当前可视区域
map.setLimitBounds(map.getBounds())
```
- 先获取当前可视区域的边界,然后将边界对象传入 `setLimitBounds`
- 矩形边界构造函数 `AMap.Bounds(southWest:LngLat, northEast:LngLat)`,参数southWest、northEast分别代表地物对象西南角经纬度和东北角经纬度值
#### 绘制常用覆盖物的方法
- 平时用的比较多的覆盖物有:`Marker、Text、Polyline、Polygon、Circle`,具体的属性方法参考文档:[覆盖物](https://lbs.amap.com/api/javascript-api/reference/overlay)
```javascript
/* 点标记样式 */
.marker{
color: red;
white-space: nowrap;
}
// ...
// 获取中心经纬度坐标
let center = map.getCenter()
// 绘制覆盖物 Marker
const text = '我是点标记'
const marker = new AMap.Marker({
position: center, // 经纬度对象,也可以是经纬度构成的一维数组,如 [116.39, 39.9]
content: `${text}`,
anchor: 'center',
offset: new AMap.Pixel(0, 0)
})
map.add(marker) // 将覆盖物加入到地图中
```
- 使用覆盖物的基本步骤:`创建覆盖物 -> add方法加入地图中`
- 还有一种常用的api,[信息窗体](https://lbs.amap.com/api/javascript-api/guide/overlays/infowindow),使用方法大同小异,有信息窗体对象的 `open()和close()` 控制窗体的开和关
```javascript
// 信息窗体
const infoWindow = new AMap.InfoWindow({
isCustom: true, // 开启自定义
content: '我是信息窗体',
autoMove: false // 是否自动调整窗体到视野内
})
infoWindow.open(map, map.getBounds().getCenter())
```
#### 绘制图片覆盖物
- 图片覆盖物可以直接使用 `Marker`,将 `content` 属性设置为 `
`
- 更好的方式是先创建 `Icon` 覆盖物
```javascript
// ...
// 绘制图片覆盖物
const icon = new AMap.Icon({
image: './demo.png',
size: new AMap.Size(32, 32), // 图片原始尺寸
imageOffset: new AMap.Pixel(2, 2), // 设置图片偏移
imageSize: new AMap.Size(28, 28) // 设置图片新尺寸
})
const imgMarker = new AMap.Marker({
position: center, // 经纬度对象,也可以是经纬度构成的一维数组[116.39, 39.9]
icon: icon,
anchor: 'center',
offset: new AMap.Pixel(0, 0)
})
map.add(imgMarker)
```
- 设置 `anchor: 'center'` 和 `offset: new AMap.Pixel(0, 0)` 将图片定位点固定在在图片正中央,方便后续的旋转角度或者平移等操作
- 图片缩小或者放大是以图片左上角为基准点,还需要设置偏移平移至正中央:`size - imageSize = 2 * imageOffset`
#### 让图形显示在合适的位置
- `setFitView(overlayList, immediately, avoid, maxZoom)` 参数均可缺省。`overlayList` 为覆盖物数组,缺省时为当前地图上添加的所有覆盖物图层;`immediately` 代表是否需要动画过程;`avoid` 代表上下左右的像素避让宽度;`maxZoom` 代表fitView之后的最大级别
```
// 让图形显示在合适的位置
map.setFitView([marker, imgMarker], true, [60, 60, 60, 60], 20)
// 限制地图区域为当前可视区域
map.setLimitBounds(map.getBounds())
```
- 限制地图区域在fitview之后以达到预期的效果
#### Vue中使用自定义组件填充content属性
- 以信息窗体的 `content` 属性为例
```javascript
// 信息窗体
// 构造一个Vue实例
const VueComponent = Vue.extend({
template: `
{{title}}
`,
data: function () {
return {
title: '我是信息窗体'
}
},
methods: {
handleClick(){
this.title = '点击了信息窗体'
}
}
})
const infoWindow = new AMap.InfoWindow({
isCustom: true, // 开启自定义
content: new VueComponent().$mount('').$el, //挂载后将元素放入content
autoMove: false // 是否自动调整窗体到视野内
})
infoWindow.open(map, map.getBounds().getCenter())
```
- 在 Vue 中使用时要注意内外 `this` 的指向问题
## 补充
#### 坐标转换
1. 坐标对比
- GPS坐标:基于WGS-84坐标系
- 高德坐标:基于GCJ-02坐标系,是在WGS-84坐标系的基础上加密过的
- 百度坐标:在GCJ-02坐标的基础上又加密了
- UTM坐标:一种平面直角坐标,将全球在平面上划分成了很多个分区

2. 官方转换api:[AMap.convertFrom](https://lbs.amap.com/api/javascript-api/reference/lnglat-to-address#t2),每次只能转换40对坐标,且调用有次数限制
3. github开源项目
- [coordtransform 坐标转换](https://github.com/wandergis/coordtransform):一个提供了百度坐标(BD09)、国测局坐标(火星坐标,GCJ02)、和WGS84坐标系之间的转换的工具模块。
- [L utm和wgs84经纬度的转换](https://github.com/jjimenezshaw/Leaflet.UTM):这里边有一个经纬度到UTM坐标的 [转换工具](https://jjimenezshaw.github.io/Leaflet.UTM/examples/input.html),可以调试用
- demo中文件夹 `utmConvert` 包含了上面两个项目的核心文件,直接使用script标签引入即可使用对象`coordtransform` 和 `L`
```javascript
// 使用坐标转换将UTM坐标转换成高德使用的GCJ-02经纬度坐标
const utm2LatLng = (x, y, zone = 49, band = 'R') => {
const item = L.utm({x, y, zone, band}) // 先将UTM坐标转成 WGS-84经纬度坐标
const coord = item.latLng()
return coordtransform.wgs84togcj02(coord.lng, coord.lat) // 再将WGS-84经纬度坐标转换成高德使用的GCJ-02经纬度坐标
}
console.log(utm2LatLng(690556.5, 3118537.6, 49, 'R')) // 49R区域包含了湖南
```
- 注意这个 `49R` 的由来

#### 图形缩放
> 前提:绘制图形的位置不重要,只是通过借助高德地图将图形绘制出来,而按原始数据绘制出来的图形占比不合适,调节zoom也无法获得合适的效果,这时就需要扩大或者缩小图形了
- 可以把经纬度坐标当成平面直角坐标系处理,偏离原位置较多
- 如有UTM数据,直接处理x和y,再转成高德经纬度绘制,更加准确
- 缩放思路:将每个坐标点乘上缩放比例系数,再平移回原中心位置,公式:`缩放后数据 = k * 缩放前数据 - (k - 1) * 缩放前图形中心点数据`,其中 k 为缩放系数,大于1为放大,小于1为缩小
#### 绘制自定义图片
- 感觉挺有意思的 [高德地图绘制自定义图片,放到地图上](https://blog.csdn.net/weixin_30822451/article/details/95268908)
#### 拾取坐标点工具
- [拾取坐标点工具](https://lbs.amap.com/console/show/picker) 在调试高德地图功能时很有用
#### 常用计算函数
> 高德官方有很多计算api,参考[数学计算库](https://lbs.amap.com/api/javascript-api/reference/math)
1. 计算角度
- 常用于根据路径计算行驶车辆旋转角
- Marker 的 angle 属性用于设置旋转角,以正北为起点,顺时针旋转,如旋转90°则设置为90
```javascript
// 计算小车旋转角度,其中a为下一个点的经纬度坐标[0, 0],b为当前点的经纬度坐标[0, 0]
calculateAngle(a, b){
if(!b) return 0
let x = a[0] - b[0]
let y = a[1] - b[1]
let arc = 0
if(y > 0){
if(x === 0){
arc = 0
}else if(x < 0){
arc = Math.atan(x / y) + 2 * Math.PI
}else{
arc = Math.atan(x / y)
}
}else if(y < 0){
if(x === 0){
arc = Math.PI
}else{
arc = Math.atan(x / y) + Math.PI
}
}else{
if(x >= 0){
arc = Math.PI / 2
}else{
arc = Math.PI * 3 / 2
}
}
return arc * 180 / Math.PI
},
```
2. 根据一系列区域点计算中心点
```javascript
// 获取区域中心点经纬度坐标
getAreaCenter(areaList){
let xSort = [...areaList]
let ySort = [...areaList]
xSort.sort((a, b) => {
return a.longitude - b.longitude
})
ySort.sort((a, b) => {
return a.latitude - b.latitude
})
return [(xSort[xSort.length - 1].longitude + xSort[0].longitude) / 2, (ySort[0].latitude + ySort[ySort.length - 1].latitude) / 2]
}
```
3. 计算两地距离
```javascript
//计算两个坐标之间的距离 单位km
getDistance(lat1, lng1, lat2, lng2) {
var radLat1 = lat1 * Math.PI / 180.0;
var radLat2 = lat2 * Math.PI / 180.0;
var a = radLat1 - radLat2;
var b = lng1 * Math.PI / 180.0 - lng2 * Math.PI / 180.0;
var s = 2 * Math.asin(Math.sqrt(Math.pow(Math.sin(a / 2), 2) +
Math.cos(radLat1) * Math.cos(radLat2) * Math.pow(Math.sin(b / 2), 2)));
s = s * 6378.137; // EARTH_RADIUS;
s = Math.round(s * 10000) / 10000;
return Number(s);
}
```