百度地图使用任意图片旋转任意角度作为贴图

更新时间: 2024-04-15 09:19:19

公司项目有个需求是要在地图上贴个航拍的照片做出类似卫星地图的效果,但是只有一张图片而且可以随时替换,也不好做瓦片地图,而且照片的角度可以任意旋转。
要实现这个功能需要解决以下问题:

  1. 百度地图怎么贴图片
  2. 图片角度如何旋转
    不卖关子,我先放出实现的效果,为了不涉及侵权,我换成了一张同事的爱犬的照片

# 实现需求

# 百度地图怎么贴图片

百度地图api中有GroundOverlay能实这个效果。 代码如下:

// 西南角和东北角
const SW = new BMap.Point(119.74455912589518, 36.92779662557118);
const NE = new BMap.Point(119.75332658767256, 36.936756872224294);

const groundOverlayOptions = {
    opacity: 1,
    displayOnMinLevel: 8,
    displayOnMaxLevel: 20
}

// 初始化GroundOverlay
const groundOverlay = new BMap.GroundOverlay(new BMap.Bounds(SW, NE), groundOverlayOptions);
const url = "(图片地址,可以是绝对地址也可以是相对地址)"

groundOverlay.setImageURL(url);
map.addOverlay(this.groundOverlay)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

然而发现问题没有,百度地图能实现的是贴方方正正没有旋转的图片,那么如何让图片旋转呢。

# 如何让贴上去的图片旋转

既然百度地图无法实现这个旋转,那我们可以从图片自身下手:

  1. 使用Image对象加载图片,得到图片的宽高
 // 首先加载图片,获取图片宽高
var path = 'xxxxxx' 
var img = new Image()
// 如果是网络图片必须加这行,不然会报错
img.setAttribute("crossOrigin",'Anonymous')
img.src = path

// 逆时针为正
let angle = 64

img.onload = function() {
    var width = this.width
    var height = this.height
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
  1. 计算图片旋转任意角度后的包围盒大小
    计算我使用了旋转矩阵,原理在另一篇文章中 旋转矩阵
function rotate([x, y], angle) {
  // 用角度计算弧度
  let rad = angle * Math.PI / 180
  let sinA =  Math.sin(rad)
  let cosA = Math.cos(rad)
  return [x * cosA - y * sinA, x * sinA + y * cosA]
}

// 比较图片4个角坐标,获取最大最小值作为包围框
function getBoundingBox(list) {
  const xList = list.map(item => item[0])
  const yList = list.map(item => item[1])
  const minX = Math.min(...xList)
  const minY = Math.min(...yList)
  const maxX = Math.max(...xList)
  const maxY = Math.max(...yList)
  const width = maxX - minX
  const height = maxY - minY
  return {
    minX,
    minY,
    maxX,
    maxY,
    width,
    height
  }
}

img.onload = function() {
    var width = this.width
    var height = this.height
    // 计算矩形旋转θ角度之后的包围盒宽高
    // 旋转矩阵为 [[cosθ, sinθ][-sinθ, cosθ]],将四个点坐标和旋转矩阵相乘
    // [x,y][[cosθ, sinθ][-sinθ, cosθ]] = [x * cosθ + y * -sinθ, x * sinθ + y * cosθ]
    // 图片四个角坐标为 左下(0,0), 左上(0,height),右下(width,0),右上(width,height)
    let posLB = rotate([0,0], angle)
    let posLT = rotate([0,height],angle)
    let posRB = rotate([width,0],angle)
    let posRT = rotate([width,height],angle)
    // 获取旋转后的包围框大小
    let {width:boundingWidth,height:boundingHeight} = getBoundingBox([posLB,posLT,posRB,posRT])
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
  1. 创建图片旋转后包围盒大小的canvas
  2. 在canvas上绘制旋转了角度的图片
  3. 使用 canvas.toDataURL() 获取url并贴到百度地图上
//.....
//.....
// 获取旋转后的包围框大小
let {width:boundingWidth,height:boundingHeight} = getBoundingBox([posLB,posLT,posRB,posRT])
// 创建canvas
var canvas = document.createElement("canvas");
canvas.width = boundingWidth;
canvas.height = boundingHeight;
var c = canvas.getContext('2d');
// 修改canvas的原点为中心点
c.translate(boundingWidth/2, boundingHeight/2)
// 旋转画布
c.rotate(-angle * Math.PI / 180)
c.drawImage(img,-width/2, -height/2,width,height);
let url = canvas.toDataURL()
// 设置GroundOverlay的图片地址
groundOverlay.setImageURL(url);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17