百度地图3d区域掩膜,最常见通用的大屏地图展现形式

需求及效果

原本项目使用的是百度地图3.0,也就是2d版本的那个地图,客户不满意觉得不够好看,让把地图改成3d的,但是我们因为另外的系统用的都是百度地图,为了保持统一只能用百度地图做

经过3天的努力,最后我终于把这个效果实现了,效果如下:
在这里插入图片描述

如何引用GL版本

为了实现这个功能,首先要将百度地图升级为GL版本。

  1. GL版本引用的百度地图API需将之前引入的替换为:
<script type="text/javascript" src="https://api.map.baidu.com/api?v=1.0&type=webgl&ak=您的密钥"></script>

初始化地图

地图初始化还是和之前一样,只不过要使用BMapGL

   const map = new BMapGL.Map("monitorMap");          // 创建地图实例
   var point = new BMapGL.Point( 128.2015353412464,49.76416045410938);  // 创建点坐标
   map.centerAndZoom(point, 7); // 设置地图中心点
   map.enableScrollWheelZoom(true); // 允许鼠标滚动缩放地图级别

将底图改为卫星地图

GL版本的百度地图有三种地图类型:

  • 地球模式(BMAP_EARTH_MAP),不过这个模式能支持的交互操作有限
  • 标准地图(BMAP_NORMAL_MAP)
  • 卫星地图(BMAP_SATELLITE_MAP)

因此,我们给地图加上配置:

const map = new BMapGL.Map("monitorMap",{
    mapType:BMAP_SATELLITE_MAP,
});   

禁用地图旋转,禁止技改变倾角

这个地图我们不能让用户自己去改变旋转角和倾角,因此需要禁用

const map = new BMapGL.Map("monitorMap",{
    mapType:BMAP_SATELLITE_MAP,
    // 禁用用户旋转
    enableRotate: false,
    // 禁用用户修改倾斜角度
    enableTilt: false,
}); 

代码修改地图倾角

做了这么多发现地图依然不是三维的

不要慌,我们手动来改一下地图倾角

map.setTilt(50);       //设置地图的倾斜角度


现在地图终于倾斜了

掩膜以及掩膜经常失败怎么解决

由于我们的效果只需要留下黑龙江省的地图,所以需要区域掩膜

我们需要获取黑龙江的边界,在百度地图中有对应API:

var bdary = new BMapGL.Boundary();
bdary.get('黑龙江', function (rs) {
     // 绘制行政区
    for (var i = 0; i < rs.boundaries.length; i++) {
        var path = [];
        var xyArr = rs.boundaries[i].split(';');
        var ptArr = [];
        for (var j = 0; j < xyArr.length; j++) {
            var tmp = xyArr[j].split(',');
            var pt = new BMapGL.Point(tmp[0], tmp[1]);
            ptArr.push(pt);
        }
        // 添加掩膜
        var mapmask = new BMapGL.MapMask(ptArr, {
            isBuildingMask: true,
            isPoiMask: true,
            isMapMask: true,
            showRegion: 'inside',
        });
        map.addOverlay(mapmask);
        // 给掩膜描边
        var border = new BMapGL.Polyline(ptArr, {
            strokeColor: '#15a6db',
            strokeWeight: 3,
            strokeOpacity: 1
        });
        map.addOverlay(border);
    }
})


现在终于把黑龙江单独拎出来了,但为什么背景是白色的?这个先不急,我们先来解决另一个问题

区域掩膜失败怎么解决

多刷新几次页面,你会发现有时候区域掩膜会失败,为什么呢?

我们分析一下,为什么有时失败有时不失败,这是不是非常像接口请求有时成功有时失败的样子,实际上百度地图也是这样做的,是因为黑龙江的边界请求有时候会失败

解决思路:
黑龙江的边界不会变的,我们把请求成功的黑龙江边界存起来就行了

// heilongjiang.js
export default {
    "boundaries": [
        "121.50682596458674, 53.341380780914506;121.51059812908427, 53.34188131383469;121.51345447111437, 53.34197641979837;121.51517077362173, 53.34203018226125;.....
    ]
}

然后将代码修改一下:

import heilongjiang from "./heilongjiang.js";

for (var i = 0; i < heilongjiang.boundaries.length; i++) {
      var path = [];
      var xyArr = heilongjiang.boundaries[i].split(';');
      var ptArr = [];
      for (var j = 0; j < xyArr.length; j++) {
        var tmp = xyArr[j].split(',');
        var pt = new BMapGL.Point(tmp[0], tmp[1]);
        ptArr.push(pt);
      }
      var mapmask = new BMapGL.MapMask(ptArr, {
        isBuildingMask: true,
        isPoiMask: true,
        isMapMask: true,
        showRegion: 'inside',
      });
      map.addOverlay(mapmask);
      // 给地图描边,不需要描边可以去掉
      var border = new BMapGL.Polyline(ptArr, {
        strokeColor: '#15a6db',
        strokeWeight: 3,
        strokeOpacity: 1
      });
      map.addOverlay(border);
    }

怎么让背景变透明

我们在初始化地图的时候加上配置:

 const map = new BMapGL.Map("monitorMap", {
    enableRotate: false,
    enableTilt: false,
    mapType:BMAP_SATELLITE_MAP,
    backgroundColor: [0, 0, 0, 0],
  });    

然后设置css样式:

#monitorMap {
    background: none!important;
}

现在背景变透明了,我在地图下方加上了自己的背景图

顶上的天空渐变怎么去掉

细心的同学会发现地图顶上有白色的天空渐变,我们并不想要它,加一个配置去掉它:

map.setDisplayOptions({
    skyColors: ['rgba(186, 0, 255, 0)','rgba(186, 0, 255, 0)']
})

将天空渐变色的透明度都改为0就可以去掉了

怎么给地图增加厚度

不想要有厚度的地图到这一步就可以结束了,后面该干嘛干嘛,但是呢,我喜欢折腾,试过很多种方法都不行,最终我使用了百度的三维模型图层,这一步需要有一点threejs的基础,不过没有也没关系,跟着我往下走就是了

引入依赖

在百度api后面追加几个script:

<script type="text/javascript"
    src="https://api.map.baidu.com/api?type=webgl&v=1.0&ak=您的密钥"></script>
<!--引入样式-->
<script type="text/javascript" src="https://mapv.baidu.com/gl/examples/static/common.js"></script>
<script type="text/javascript" src="https://mapopen-pub-jsapigl.bj.bcebos.com/newThree/three.min.js"></script>

<script type="text/javascript" src="https://code.bdstatic.com/npm/mapvgl@1.0.0-beta.151/dist/mapvgl.min.js"></script>
<script type="text/javascript" src="https://code.bdstatic.com/npm//mapvgl@1.0.0-beta.151/dist/mapvgl.threelayers.min.js"></script>

修改地图初始化配置

const map = new BMapGL.Map("monitorMap", {
    forceRenderType: 'webgl',
    coordsType: 5, // coordsType指定输入输出的坐标类型,3为gcj02坐标,5为bd0ll坐标,默认为5。
    enableRotate: false,
    enableTilt: false,
    mapType:BMAP_SATELLITE_MAP,
    backgroundColor: [192, 214, 213, 0],
  });

初始化三维图层

var view = new mapvgl.View({
    map: map
});

threeLayer = new mapvgl.ThreeLayer({ notUpdateSize: false });
view.addLayer(threeLayer);

这里的threeLayer就相当于threejs中的scene了,后面的代码和在threejs里写差不多

添加灯光

var lights = [];
// 创建点光源
lights[0] = new THREE.PointLight(0xffffff, 1, 0);
// 给点光源设置位置
lights[0].position.set(0, -1000, 1000);
// 将光源添加到场景中
threeLayer.scene.add(lights[0]);

使用threejs的挤出几何体 ExtrudeGeometry 实现黑龙江省的厚度

根据黑龙江省的边界创建挤出几何体mesh,该几何体的高度为46000,因此将该几何体下移46001让该几何体位于地图的下方。

const shape = new THREE.Shape()

  for (var i = 0; i < heilongjiang.boundaries.length; i++) {
    var xyArr = heilongjiang.boundaries[i].split(';');
    for (var j = 0; j < xyArr.length; j++) {
      var tmp = xyArr[j].split(',');
      // 将经纬度坐标转换为平面坐标
      var pt = map.lnglatToMercator(tmp[0], tmp[1]);
      if(j == 0) {
        shape.moveTo( pt[0], pt[1] );
      }else{
        shape.lineTo( pt[0], pt[1] );
      }
    }
  }

  const extrudeSettings = {
    steps: 2,
    depth: 46000
  };

  // 创建挤出几何体的geometry
  const geometry1 = new THREE.ExtrudeGeometry( shape, extrudeSettings );
  // 创建挤出几何体的材质
  const material1 = new THREE.MeshLambertMaterial( { color: 0x15a6db, transparent:true,opacity:0.5 } );
  // 创建地图厚度mesh
  const mesh = new THREE.Mesh( geometry1, material1 ) ;
  // 修改mesh的位置
  mesh.position.z = -46001
  // 将mesh添加到threeLayer中去
  threeLayer.add( mesh );

创建三维图层的动画循环

写过threejs的同学都知道,threejs绘制物体是需要有动画循环的,动画循环中更新画面

function animate() {
    requestAnimationFrame(animate);
    threeLayer.update();
}
requestAnimationFrame(animate);

现在我们的地图有厚度了,如果想要更厚,可以多挤出一点距离

其实这里是有些问题的,你会发现卫星地图也变蓝一些了,这是因为三维模型图层在地图图层的上方,我还没有找到解决这个的办法,因此将模型的透明度调整为了0.5,结果还挺好看的,就算了吧

地图缩小总是自动修改倾角怎么解决


现在用鼠标滚轮缩放一下地图,哎!哎???百度地图怎么自动给我修改了倾斜角!!!

然后我翻遍了api文档和demo都没有找到怎么调整这个参数

过程就不说了,我看了百度地图api的代码找了很久最终才实现,现在来说一下解决方案:

  1. 用浏览器打开 https://api.map.baidu.com/api?type=webgl&v=1.0&ak=您的密钥 秘钥要填自己的哦,打开是下面的样子

  2. 然后用浏览器打开红框圈中的地址,将这个js文件下载到本地,引入到项目中

  3. 打开刚刚的js文件,全局搜索 _enableTiltZoom ,找到这一行

    将其修改为

 this._enableTiltZoom = 1;

然后限制一下地图最小的缩放尺寸

const map = new BMapGL.Map("monitorMap", {
    forceRenderType: 'webgl',
    coordsType: 5, // coordsType指定输入输出的坐标类型,3为gcj02坐标,5为bd0ll坐标,默认为5。
    enableRotate: false,
    enableTilt: false,
    mapType:BMAP_SATELLITE_MAP,
    backgroundColor: [192, 214, 213, 0],
    minZoom: 5
  });

好了,现在无论怎么缩放都不会修改倾斜角了

如何在地图上布三维点

如何在地图上布点呢,一般的点百度地图都有demo,这里我就不说了

大家都知道百度地图的marker在超过两百个以后会巨卡,之前我也写过2d版本的百度地图海量点方案,这里我写一下3d版本的

我这里会准备一个变量存储点的坐标数组,然后遍历数组往threeLayer中添加 Cone 当做点位:

const pointList = [] // 存储三维点对象,后面射线拾取的时候要用
let pointWidth = 10000 // 三维点的基础宽度
let pointHeight = 50000 // 三维点的基础高度
let pointZ = 25000 // 三维点的z轴位置

// 颜色数组,根据不同的status状态画不同颜色的点
const colorList = [0xEAEAEA, 0x1CE945, 0xEDE11C, 0xE9860D, 0xF0323F]
// 测试数据
const bridgeListData = [
    {
        bridgeName: '测试1'latitude:47.07133734521393,
        longitude:129.09809887846538
    },
    {
        bridgeName: '测试2'latitude:45.7550708,
        longitude:126.4918776
    }
]

for(let i = 0;i<bridgeListData.length;i++) {
    const {longitude, latitude, bridgeName, status} = bridgeListData[i]
    let color = colorList[status + 1]
    const geometry = new THREE.ConeGeometry( pointWidth, pointHeight, 3 );
    const material = new THREE.MeshLambertMaterial( {color: color} );
    const cone = new THREE.Mesh( geometry, material );
    // 将经纬度转换为平面坐标
    const point = map.lnglatToMercator(longitude,latitude)
    // 设置点的空间位置
    cone.position.x = point[0];
    cone.position.y = point[1];
    cone.position.z = pointZ;
    // 将点旋转270度,让锥形的尖头朝下
    cone.rotateX(270 / 180 * Math.PI);
    // 将数据存储到点对象上
    cone.data = bridgeListData[i]
    pointList.push(cone)
    threeLayer.add(cone);
  }

这样点是加上去了,但是缩放地图之后,点会变得巨大

三维点如何保持匀称的大小

如何保持点的大小在视觉上不变呢?

其实我也没有做到完全不变,让大小完全不变的那个公式我暂时还没有摸索出来

原理其实就是在地图缩放的时候,将点的尺寸根据地图当前的zoom属性做一下修改就好了

const pointList = []
let pointWidth = 10000
let pointHeight = 50000
let pointZ = 25000

const colorList = [0xEAEAEA, 0x1CE945, 0xEDE11C, 0xE9860D, 0xF0323F]
const bridgeListData = [
    {
        bridgeName: '测试1'latitude:47.07133734521393,
        longitude:129.09809887846538
    },
    {
        bridgeName: '测试2'latitude:45.7550708,
        longitude:126.4918776
    }
]

// 根据地图的层级,返回不同的scale值
function getScale(level) {
  if(level > 1 && level <= 10) {
    return (15 - level)/level
  }else if(level > 10 && level <= 12){
    return (13 - level)/level
  }else if(level > 12 && level <= 14) {
    return 1/(level * (level - 11))
  }else if(level > 14 && level <= 16) {
    return 1/(level * (level - 11) * 2)
  }else if(level > 16 && level <= 18) {
    return 1/(level * (level - 11) * 3)
  }else if(level > 18) {
    return 1/(level * (level - 11) * 4)
  }
  return 1/level
}

const level = map.getZoom()
const scale = getScale(level)

// 绘制点
for(let i = 0;i<bridgeListData.value.length;i++) {
  const {longitude, latitude, bridgeName, status, bridgeUniqueCode} = bridgeListData.value[i]
  let color = colorList[status + 1]
  const geometry = new THREE.ConeGeometry( pointWidth, pointHeight, 3 );
  const material = new THREE.MeshLambertMaterial( {color: color} );
  const cone = new THREE.Mesh( geometry, material );
  // 缩放点的大小
  cone.scale.x = scale
  cone.scale.y = scale
  cone.scale.z = scale
  const point = map.lnglatToMercator(longitude,latitude)
  cone.position.x = point[0];
  cone.position.y = point[1];
  // 点的z坐标也要缩放
  cone.position.z = pointZ * scale;
  cone.rotateX(270 / 180 * Math.PI);
  cone.data = bridgeListData.value[i]
  pointList.push(cone)
  threeLayer.add(cone);
}

// 地图缩放层级的时候更改点的大小
map.addEventListener('zoomend', function(e) {
    const level = map.getZoom()
    const scale = getScale(level)
    pointList.forEach(cone => {
      cone.scale.x = scale
      cone.scale.y = scale
      cone.scale.z = scale
      cone.position.z = pointZ*scale
    })
  });

三维点的点击事件

然后就可以给三维点添加点击事件了,这里我在map的click事件中,使用raycaster射线法进行拾取

var raycaster = new THREE.Raycaster();
var mouse = new THREE.Vector2();

// 获取地图的宽高
const size = map.getSize()
const canvasWidth = size.width
const canvasHeight = size.height

map.addEventListener('mousedown', function (e) {
  var fs = [...pointList]

  // 归一化鼠标位置,范围-1 1,原点在中心
  mouse.x = (e.offsetX / canvasWidth) * 2 - 1;
  mouse.y = -(e.offsetY / canvasHeight) * 2 + 1;

  raycaster.setFromCamera(mouse, threeLayer.camera);
  if (fs.length>0) {
    // 射线检测的对象为三维点
    var intersects = raycaster.intersectObjects(fs);
    console.log(intersects)
  }
});

这里注意,归一化的时候,一定要用地图本身的宽高,而不是整个屏幕的宽高,不然射线拾取会有问题的!

好啦,到这里效果就和我开头的一样啦,只是少了弹窗的部分,弹窗我是用html写的,和这次的主题关系不大~

  • 30
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
在Vue大屏项目中使用百度地图可以按照以下步骤进行: 1. 首先,安装百度地图 JavaScript API。可以通过以下方式在Vue项目中引入百度地图API: ```bash npm install baidu-map-gl --save ``` 2. 在Vue项目的入口文件(通常是`main.js`)中引入百度地图API: ```javascript import BaiduMap from 'vue-baidu-map' Vue.use(BaiduMap, { ak: 'your-baidu-map-api-key' }) ``` 在上述代码中,您需要将 `your-baidu-map-api-key` 替换为您自己的百度地图API密钥。 3. 创建一个Vue组件来显示百度地图,并在模板中使用它: ```vue <template> <div> <baidu-map class="map-container" :center="center" :zoom="zoom"> <bm-marker v-for="(marker, index) in markers" :position="marker.position" :label="marker.label" :icon="marker.icon" :clickable="true" :key="index"></bm-marker> </baidu-map> </div> </template> <script> export default { data() { return { center: { // 地图中心点坐标 lng: 116.404, lat: 39.915 }, zoom: 11, // 地图缩放级别 markers: [ // 标注点集合 { position: { lng: 116.404, lat: 39.915 }, // 标注点坐标 label: { content: '标注点1', offset: { width: 0, height: -36 } // 标注点文本偏移量 }, icon: { url: 'path/to/your-marker-icon.png', // 标注点图标路径 size: { width: 36, height: 36 }, // 标注点图标尺寸 offset: { width: -18, height: -36 } // 标注点图标偏移量 } }, // 其他标注点... ] } } } </script> <style> .map-container { width: 100%; height: 600px; } </style> ``` 在上述代码中,您可以根据自己的需求设置地图的中心点、缩放级别和标注点集合。同时,您还可以自定义标注点的图标和标签内容。 4. 最后,使用该组件在您的大屏项目中显示百度地图: ```vue <template> <div> <!-- 其他大屏组件... --> <MapComponent /> <!-- 其他大屏组件... --> </div> </template> <script> import MapComponent from './MapComponent.vue' // 替换为您创建的百度地图组件路径 export default { components: { MapComponent }, // 其他配置项... } </script> ``` 通过上述步骤,您就可以在Vue大屏项目中使用百度地图,并在地图上显示多个标注点了。请注意,以上代码仅提供了一个简单的示例,您可以根据自己的需求进行调整和扩展。另外,如果需要使用地图的其他功能,可以参考百度地图的API文档进行相应的调用和配置。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值