美文网首页
vue 高德地图Loca.GeoJSONSource、Loca.

vue 高德地图Loca.GeoJSONSource、Loca.

作者: 东扯葫芦西扯瓜 | 来源:发表于2021-06-17 20:03 被阅读0次

    创建地图

    假设已经正确引入了高德地图,这里使用2.0版本,注意了,1.4.x版本的使用和2.x版本的使用方式不一样。有很多地方不兼容哦。

    话说3D效果这一块,高德是真比不上百度地图哦,要不是项目一直用的高德地图,怕影响数据,就真想换百度地图了。百度地图有很多地方,地级市县都有3D效果了,但是高德没有。高德只有省会城市有3D楼

    引入高德地图还需要加上Loca版本,plugin插件里面也要包含Map3D插件。可以参考这里 vue 使用amap-jsapi-loader加载高德地图

    然后我们创建地图。注意这次我们是要绘制3D楼房,所以初始化地图时必须指定 viewMode: '3D'

    这里还加个小小的效果,旋转地图动画

    友情提醒:水波效果图,高德地图里面叫呼吸图,不知道是哪位大佬起的名字。话说叫水波纹不是更形象吗?叫呼吸图,真的想不出来是者效果。

    <template>
        <div>
            <el-switch v-model="isRotate" active-color="#FC982A" @change="rotateChange"/>
            <div class="amap" id="amap"></div>
        </div>
    </template>
    <script>
    import reqMapList from '../../../api'
    import transparentImg from '../../assets/transparent.svg'
    import {mapState} from 'vuex'
    import texture from "../../assets/images/breath_red.png";
        export default{
            data(){
                return{
                     center:[116.438202,40.138265],
                     isRotate: false,//是否旋转
                     }
            },
            computed:{
                ...mapState(['lngLatArr'])
            },
              mounted() {
                if(window.AMap && window.Loca){
                  this.loadMap()
                  return
                }
                this.awaitAmapLoading()
              },
              methods: {
              //定时加递归,确保有AMap和Local实例后再加载地图
                awaitAmapLoading(){
                  let interval=setInterval(()=>{
                    if(window.AMap&& window.Loca){
                      clearTimeout(interval)
                      this.loadMap()
                    }
                  },500)
                },
                async loadMap() { // 加载地图 需要在挂载后
                  let {center} = this
                  let map = new AMap.Map('amap', {
                    center,
                    zooms: [2, 20],
                    zoom: 16,
                    viewMode: '3D',
                    pitch: 50,
                    rotation: 35,
                  })
                  let loca = new Loca.Container({
                    map,
                  })
                  map.setRotation(395,false,4000)
                  this.map = map           
                  let {mapAllList} = await reqMapList ()
                  this.add3DBuild(loca) //绘制3d楼函数
                  this.drawRipple(loca, mapAllList) //绘制水波
                  this.addLabelMarker(mapAllList)//绘制文字标注
                },
                //旋转切换
                rotateChange(value) {
                  let {map} = this
                  if (!value) {
                    clearInterval(this.mapInterval)
                    let rotate = map.getRotation()
                    let remainderRotate = (rotate - 35) % 360
                    let backRotate = rotate - remainderRotate
                    map.setRotation(backRotate,false,8000*remainderRotate/360)
                    return
                  }
                  let n=0
                  map.setRotation(map.getRotation()+395,false,8000)
                  let currentRotation=map.getRotation()+395
                  this.mapInterval=setInterval(()=>{
                    n++
                    map.setRotation(currentRotation+395*n,false,8000)
                  },8000)       
                },
                addLabelMarker(mapAllList){
                  let icon = {
                    // 图标类型,现阶段只支持 image 类型
                    type: 'image',
                    // 图片 url
                    image: transparentImg,
                    // 图片尺寸
                    size: [30, 30],
                    // 图片相对 position 的锚点,默认为 bottom-center
                    anchor: 'center',
                  }
                  let text = {
                    // 文字方向,有 icon 时为围绕文字的方向,没有 icon 时,则为相对 position 的位置
                    direction: 'right',
                    // 在 direction 基础上的偏移量
                    // 文字样式
                    style: {
                      // 字体大小
                      fontSize: 12,
                      // 字体颜色
                      fillColor: '#248BFE',
                    }
                  }
                  this.addLabelMarker(mapAllList,icon,text)
                },
            //添加3d建筑物
                add3DBuild(loca){
                    let {lngLatArr}=this
                    let features=lngLatArr.reduce((result,current)=>{
                        result.push({
                            "type": "Feature",
                            "geometry": {
                                "type": "Polygon",
                                "coordinates": [
                                    current
                                ]
                            }
                        })
                        return result
                    },[])
                    loca.pointLight = {
                        color: 'rgb(100,100,100)',
                        position: [112.053413,27.778557, 200],
                        intensity: 3,
                        // 距离表示从光源到光照强度为 0 的位置,0 就是光不会消失。
                        distance: 50000,
                    }
                    let geo = new Loca.GeoJSONSource({
                        data:{
                            "type": "FeatureCollection",
                            features
                        }
                    })
                    let pl = new Loca.PolygonLayer({
                        zIndex: 120,
                        opacity:1,
                        shininess: 10,
                        hasSide: true,
                    })
                    pl.setSource(geo)
                    pl.setStyle({
                        topColor:'#dddddd',
                        sideColor: '#cccccc',
                        height:300,
                        altitude: 1,
                    })
                    loca.add(pl)
                },
                //绘制波纹
                drawRipple(loca,mapAllList){
                    let geoFeatures=mapAllList.reduce((result,current)=>{
                        let {lon,lat,address}=current
                        if(lon && lat){
                            result.push({
                                "type": "Feature",
                                "geometry": {
                                    "type": "Point",
                                    "coordinates": [lon, lat]
                                },
                                "properties": {
                                    "name": address,
                                }
                            })
                        }
                        return result
                    },[])
                    let geoLevelF = new Loca.GeoJSONSource({
                        data: {
                            "type": "FeatureCollection",
                            "features":geoFeatures
                        },
                    })
                    let breathRed = new Loca.ScatterLayer({
                        loca,
                        zIndex: 9130,
                        opacity: 1,
                        visible: true,
                        zooms: [2, 22],
                    })
                    breathRed.setSource(geoLevelF)
                    breathRed.setStyle({
                        unit: 'meter',
                        size: [100, 100],
                        borderWidth: 0,
                        texture,//动画图片,后面会做说明
                        duration: 500,
                        animate: true,
                    })
                    // 启动渲染动画
                    loca.animate.start()
                },
                //添加文字标注
                addLabelMarker(mapAllList,icon,text){
                    let labelsMarkerArr=mapAllList.reduce((result,current)=>{
                        let {name,lon,lat}=current
                        if(lon && lat){
                            let labelMarker = new AMap.LabelMarker({
                                name: name, // 此属性非绘制文字内容,仅最为标识使用
                                position: [lon, lat],
                                zIndex: 16,
                                // 将第一步创建的 icon 对象传给 icon 属性
                                icon,
                                // 将第二步创建的 text 对象传给 text 属性
                                text:{
                                    ...text,
                                    content:name
                                },
                            })
                            labelMarker.on('click', function(e){
                               console.log(e)
                            })
                            result.push(labelMarker)
                        }
                        return result
                    },[])
    
                    let labelsLayer = new AMap.LabelsLayer({
                        zooms: [3, 20],
                        zIndex: 1000000,
                        // 该层内标注是否避让
                        collision: true,
                        // 设置 allowCollision:true,可以让标注避让用户的标注
                        allowCollision: true,
                    })
                    // 批量添加 labelMarker
                    labelsLayer.add(labelsMarkerArr)
                    this.map.add(labelsLayer)
                }
              },
              beforeDestroy() {
                clearInterval(this.interval)
                clearInterval(this.mapInterval)
                this.map=null
              }
        }
    </script>
    

    这里说下lngLatArr的数据结构,这是楼房的经纬度,由于是自定义画的,因此需要自己去获取。

    获取有一点规则,首先要顺时针获取四个点,然后需要画闭环的楼房,所以第五个点和第一个点是同一个坐标。

    lngLatArr是一个三维数组。说道这里真忍不住吐槽下高德地图的文档,反正我没找到有关于数据结构的说明,做的时候全靠猜,靠试。

    如果自己项目里没有配置有逝去经纬度的页面,可以搜索高德地图拾取经纬度,去高德地图相关页面获取,页面地址 https://lbs.amap.com/tools/picker

    示例结构如下:

     blLngLatArr:Object.freeze([
            [
                [116.42095,40.138003],
                [116.445755,40.14148],
                [116.422666,40.131703],
                [116.441549,40.126584],
                [116.42095,40.138003],
            ],
            [
                [116.463093,40.144236],
                [116.476139,40.144761],
                [116.465668,40.140234],
                [116.476396,40.140168],
                [116.463093,40.144236],
            ],   
        ]),
    

    绘制水波纹最后一定要启动动画,即loca.animate.start(),奇怪的是,不加这行也有动画,只不过,这动画怪怪的,运行两下就不动了。

    texture 是动画图片,这里绘制的水波纹图效果,实际上是不断变化图片的位置形成的。我这里直接用了官方的示例图片,如下!


    breath_red.png

    优化

    虽然这样是可以了,但是你会发现,切换页面以后,再切换回来,3D图没有了,水波图也没有了!!
    这已经是一个很严重的bug了,但是官方问答貌似没有很明确的说明。

    原因是:loca实例只能创建一个,虽然这里创建loca实例时只是使用了局部变量,但是高德地图生成了全局的loca实例。当我们切换页面再切回来时,又去创建一个实例,这时就有两个了,然后就是看到的效果,图出不来。

    最终的解决方案是,当前页面离开时,销毁loca实例。销毁不是直接置为Null就可以,而是要调loca的destroy方法。

    然后说下水波纹图,同样是,需要移除水波纹图层,不然会报错:<span style="color:red;">cannot read property 'getZoom' of null</span>。

    最终解决方案如下:
    先将loca和breathRed挂载到this上,然后销毁他们

     beforeDestroy(){
        this.loca.remove(this.breathRed)
        this.loca.destroy()
     }
    

    相关文章

      网友评论

          本文标题:vue 高德地图Loca.GeoJSONSource、Loca.

          本文链接:https://www.haomeiwen.com/subject/rietyltx.html