高德地图通过图层layer实现对海量点的可视化渲染

news2024/11/15 15:59:19

一、可视化海量点应用场景

在正文开始之前我先说说我为啥会使用这个技术来实现数据的可视化。
事情是这样的,我接手了一个项目,里面有个需求是在地图上标记出他们公司的产品的使用分布。我接手的时候呢,我前面的那位大哥是使用marker点覆盖物,加上for循环来渲染实现的,可能他在维护这个项目的时候,公司的产品上线的比较少,最多的时候也不超过2000个,所以通过for循环marker也没出现什么卡顿现象。
可到我这里,好家伙,一下子数据飙到1w多,进那个页面之后直接卡死,浏览器直接崩溃了。所以说通过for循环marker的方式在数据量小的时候还可以,在大数据面前显然是不可取的。

在高德官方呢也给出了解决方案,

  • 一个是通过MassMarks海量点来解决
  • 一个是通过layer加上 Loca.GeoJSONSource的方式处理

二、示例代码

我这里采用的是第二种方式,这是我根据官方示例自己在vue项目中实现的demo效果
官网示例:https://lbs.amap.com/demo/loca-v2/demos/cat-icon/icon_traffic

  • 可以显示和影藏点
  • 鼠标移动到点上时样式改为手的样式
  • 点击点覆盖物时弹出信息窗体
    在这里插入图片描述
    在这里插入图片描述
    好了 先不说别的直接上代码吧,整体代码给了之后,再分析一下一些细节注意点。

1. 地图初始化mixin抽取

  • mapinit.vue
import AMapLoader from "@amap/amap-jsapi-loader";
window._AMapSecurityConfig = {
  securityJsCode: "xxxxxxxxxxx", // '「申请的安全密钥」',
};

const mapInit = {
  data() {
    return {
      map: null,
      AMap: null,
      Loca: null,
      msg: "hello",
    };
  },
  methods: {
    async initAMap() {
      this.AMap = await AMapLoader.load({
        key: "xxxxxxxxxxxxxxxxxxxxx", // 申请好的Web端开发者Key,首次调用 load 时必填
        version: "2.0", // 指定要加载的 JSAPI 的版本,缺省时默认为 1.4.15
        plugins: [
          "AMap.Scale",
          "AMap.ToolBar",
          "AMap.ControlBar",
          "AMap.Geocoder",
          "AMap.Marker",
          "AMap.CitySearch",
          "AMap.Geolocation",
          "AMap.AutoComplete",
          "AMap.InfoWindow",
        ], // 需要使用的的插件列表,如比例尺'AMap.Scale'等
        Loca: {
          // 是否加载 Loca, 缺省不加载
          version: "2.0.0", // Loca 版本,缺省 1.3.2
        },
      });
      this.map = new AMap.Map("amapcontainer", {
        willReadFrequently: true,
        //设置地图容器id
        resizeEnable: true,
        zoom: this.zoom, // 地图显示的缩放级别
        viewMode: "3D", // 使用3D视图
        zoomEnable: true, // 地图是否可缩放,默认值为true
        dragEnable: true, // 地图是否可通过鼠标拖拽平移,默认为true
        doubleClickZoom: true, // 地图是否可通过双击鼠标放大地图,默认为true
        zoom: 11, //初始化地图级别
        center: [116.397428, 39.90923], // 初始化中心点坐标 北京
        // mapStyle: "amap://styles/darkblue", // 设置颜色底层
      });

    },
    // 修改鼠标样式
    changeCursor(layer) {
      //监听鼠标移动事件,如果移动到图层上的元素,就改变鼠标样式为手的样式
      this.map.on("mousemove", (e) => {
        let features = layer.queryFeature(e.pixel.toArray());
        if (features) {
        this.map.setDefaultCursor("pointer");
        } else {
        this.map.setDefaultCursor("default");
        }
    });
    }
  },
  mounted() {
    //DOM初始化完成进行地图初始化
    // this.initAMap();
  },
  created() {   
  }
};

export default mapInit;

2. 地图点的数据准备

高德官方是的数据:https://a.amap.com/Loca/static/loca-v2/demos/mock_data/events.js
在这里插入图片描述
我是把它搞下来,在本地新建了个文件,然后导入使用的

3. demo组件

<template>
    <div class="local-container">
        <div id="amapcontainer" style="width: 100%; height: 880px"></div>
        <div class="button-list">
            <el-button type="primary" @click="layer.show()">显示图层</el-button>
            <el-button type="primary" @click="layer.hide()">隐藏图层</el-button>
        </div>
    </div>
</template>

<script>
import mapinit from "@/mixin/mapinit.js";
import events from "@/count/event.js";
console.log('events', events)
export default {
    mixins: [mapinit],
    data() {
        return {
            layer: null,
        };
    },
    created() {
        //可以直接使用mixin里面定义变量
        console.log("create--one", this.msg);
    },
    methods: {
        async mapExtend() {
            let loca = new Loca.Container({ //创建一个容器
                map: this.map,
            });
            // let data = this.getTestData()
            let data = this.getEventsCollection();
            console.log('data', data)
            let geo = new Loca.GeoJSONSource({ //数据源
                data: data,
            });

            let layer = new Loca.IconLayer({ //图层
                zIndex: 10, //图层层级
                opacity: 1, //透明度
                visible: false, //是否显示
            });

            let trafficIcons = {
                1: 'https://a.amap.com/Loca/static/loca-v2/demos/images/traffic-control.png',
                2: 'https://a.amap.com/Loca/static/loca-v2/demos/images/jam.png',
                3: 'https://a.amap.com/Loca/static/loca-v2/demos/images/construction.png',
                4: 'https://a.amap.com/Loca/static/loca-v2/demos/images/close.png',
                5: 'https://a.amap.com/Loca/static/loca-v2/demos/images/fog.png',
                0: 'https://a.amap.com/Loca/static/loca-v2/demos/images/accident.png',
            };
            layer.setSource(geo); //设置数据源
            layer.setStyle({
                unit: 'px',
                icon: (index, feature) => {
                    let data = feature.properties.rawData;
                    let url = trafficIcons[data.type % Object.keys(trafficIcons).length];
                    return url;
                },
                // icon: 'https://a.amap.com/Loca/static/loca-v2/demos/images/traffic-control.png',
                iconSize: [40, 40],
                offset: [0, -40],
                rotation: 0,
            })

            loca.add(layer);
            this.map.on('complete', function () {
                setTimeout(function () {
                    layer.show();
                    layer.addAnimate({
                        key: 'offset',
                        value: [0, 1],
                        easing: 'Linear',
                        transform: 500,
                        random: true,
                        delay: 9000,
                    });
                    layer.addAnimate({
                        key: 'iconSize',
                        value: [0, 1],
                        easing: 'Linear',
                        transform: 500,
                        random: true,
                        delay: 9000,
                    });
                }, 800);
            });
            loca.animate.start();
            layer.show(); //显示图层
            // 拾取测试
            this.map.on('click', (e) => {
                const feat = layer.queryFeature(e.pixel.toArray());
                console.log('feat', feat);
                if (feat) {
                    layer.setStyle({
                        unit: 'px',
                        icon: (index, feature) => {
                            let data = feature.properties.rawData;
                            let url = trafficIcons[data.type % Object.keys(trafficIcons).length];
                            return url;
                        },
                        iconSize: (i, feature) => {
                            if (feature === feat) {
                                return [60, 60];
                            }
                            return [40, 40];
                        },
                    });
                    // 为当前的feat信息,添加一个弹窗
                    const infoWindow = new this.AMap.InfoWindow({
                        content: `<div style="border: 1px solid black; padding: 10px; width: 200px; border-radius: 5px;">
                                    <h2 style="margin-bottom: 10px;">个人信息</h2>
                                    <p><strong>姓名:</strong> 张三</p>
                                    <p><strong>年龄:</strong> 25岁</p>
                                    <p><strong>性别:</strong> 男</p>
                                    <p><strong>地址:</strong> 北京市朝阳区</p>
                                </div>`,
                        offset: new this.AMap.Pixel(0, -30),
                    });
                    infoWindow.open(this.map, feat.coordinates);

                }
            });

            this.changeCursor(layer);

            this.layer = layer;
        },
        getEventsCollection() {
            let _events = events[0].events;
            let list = _events.map(e => {
                let ll = e.lngLat.split(',');
                let arr = [parseFloat(ll[0]), parseFloat(ll[1])] //lng,lat
                return {
                    "type": "Feature",
                    "properties": {
                        rawData: e
                    },
                    "geometry": {
                        "type": "Point",
                        "coordinates": arr
                    }
                }
            })
            // console.log('list', list)
            let data = {
                "type": "FeatureCollection",
                "features": list,
            };
            return data;
        },
        getTestData() {
            let list = [];
            for (let i = 0; i < 200; i++) {
                let fList = [];
                for (let j = 0; j < 200; j++) {
                    fList.push([
                        Number((115.601 + i * 0.002).toFixed(5)), Number((40.32 + j * 0.002).toFixed(5))
                    ]);
                }
                list.push({
                    "type": "Feature",
                    "properties": {
                        "name": i,
                    },
                    "geometry": {
                        "type": "MultiPoint",
                        "coordinates": fList
                    }
                });
            }
            list.push({
                "type": "Feature",
                "properties": {
                    "name": 'HIIII',
                },
                "geometry": {
                    "type": "Point",
                    "coordinates": [115.201, 40.302]
                }
            })

            let data = {
                "type": "FeatureCollection",
                "features": list,
            };
            return data;
        }
    },
    async mounted() {
        await this.initAMap();
        await this.mapExtend();
        // console.log('this.map', this.map)
        // console.log('this.AMap', this.AMap)
        // console.log('this.Loca', this.Loca)
        // this.map.setMapStyle('amap://styles/darkblue')
        // 设置地图中心点为北京
        this.map.setCenter([116.397428, 39.90923]);
    },
};
</script>

<style lang="less" scoped>
.local-container {
    position: relative;
    width: 100%;
    .button-list {
        position: absolute;
        top: 20px;
        left: 20px;
        z-index: 100;
    }
}
</style>

4. 在页面中使用

<template>
  <div>
    <!-- 海量点 -->
    <local-container></local-container>
  </div>
</template>
<script>

import LocalContainer from "@/components/LocalContainer";

export default {
  name: "homeView",
  components: { LocalContainer },
}
</script>

<style lang="less" scoped>
</style>

三、核心功能分析

1. 海量点的数据处理和图层的添加。

            let loca = new Loca.Container({ //创建一个容器
                map: this.map,
            });
            // let data = this.getTestData()
            let data = this.getEventsCollection();
            console.log('data', data)
            let geo = new Loca.GeoJSONSource({ //数据源
                data: data,
            });

            let layer = new Loca.IconLayer({ //图层
                zIndex: 10, //图层层级
                opacity: 1, //透明度
                visible: false, //是否显示
            });
			layer.setSource(geo); //设置数据源
			loca.add(layer); //将图层添加到容器
			layer.show(); //显示图层

		// 处理数据的方法
        getEventsCollection() {
            let _events = events[0].events;
            let list = _events.map(e => {
                let ll = e.lngLat.split(',');
                let arr = [parseFloat(ll[0]), parseFloat(ll[1])] //lng,lat
                return {
                    "type": "Feature",
                    "properties": {
                        rawData: e
                    },
                    "geometry": {
                        "type": "Point",
                        "coordinates": arr
                    }
                }
            })
            // console.log('list', list)
            let data = {
                "type": "FeatureCollection",
                "features": list,
            };
            return data;
        },

我们从上面的地址拿到的数据是这样的结构
在这里插入图片描述
我们需要处理成这样的结构
在这里插入图片描述
这是因为Loca.GeoJSONSource类需要这种的数据结构。
这里介绍一下Loca.GeoJSONSource
高德地图中的Loca.GeoJSONSource是一个数据源类型对象,用于处理和处理GeoJSON数据。

GeoJSON是一种面向地理数据的开放标准格式,基于JSON(JavaScript对象表示法)。其编码各种地理数据结构,如:点(Point),线(LineString),面(Polygon)等地理数据结构。

Loca.GeoJSONSource的主要用途是处理地图上的数据信息,可以用它来展示各种地理信息数据,如道路、建筑物、行政区域等。

使用Loca.GeoJSONSource首先需要初始化一个Loca.Visual的对象,然后调用其source函数,指定数据源类型为 GeoJSON数据源。如果需要获取远程的GeoJSON数据则需要在参数中指定URL,如果需要处理本地的GeoJSON数据则需传入本地GeoJSON对象。

举例说明:

var layer = new Loca.LineLayer({
    map: map,
    zIndex: 120,
});

layer.source({
    type: 'GeoJSON',
    data: 'https://example.com/data.geojson'
});

这里首先创建了一个Loca.LineLayer对象,并指定了地图展示对象和图层层级。然后调用source方法设置了数据源,数据源类型为GeoJSON,数据路径为指定的网址。

如果你要处理本地对象的数据,代码如下:

var geojson = {
    "type":"FeatureCollection",
    "features":[{
        "type":"Feature",
        "geometry":{
            "type":"Point",
            "coordinates":[123,45]
        },
        "properties":{}
    }]
};
layer.source({
    type: 'GeoJSON',
    data: geojson
});

这里首先定义了一个符合GeoJSON标准的本地对象geojson ,然后调用source方法设置了数据源,数据源类型为GeoJSON,数据则直接传入初始化的geojson对象。

2. 改变鼠标样式

// 修改鼠标样式
    changeCursor(layer) {
      //监听鼠标移动事件,如果移动到图层上的元素,就改变鼠标样式为手的样式
      this.map.on("mousemove", (e) => {
        let features = layer.queryFeature(e.pixel.toArray());
        if (features) {
          this.map.setDefaultCursor("pointer");
        } else {
          this.map.setDefaultCursor("default");
        }
      });
    },

3. 点击图标放大并展示信息

            this.map.on('click', (e) => {
                const feat = layer.queryFeature(e.pixel.toArray());
                console.log('feat', feat);
                if (feat) {
                    layer.setStyle({
                        unit: 'px',
                        icon: (index, feature) => {
                            let data = feature.properties.rawData;
                            let url = trafficIcons[data.type % Object.keys(trafficIcons).length];
                            return url;
                        },
                        iconSize: (i, feature) => {
                            if (feature === feat) {
                                return [60, 60];
                            }
                            return [40, 40];
                        },
                    });
                    // 为当前的feat信息,添加一个弹窗
                    const infoWindow = new this.AMap.InfoWindow({
                        content: `<div style="border: 1px solid black; padding: 10px; width: 200px; border-radius: 5px;">
                                    <h2 style="margin-bottom: 10px;">个人信息</h2>
                                    <p><strong>姓名:</strong> 张三</p>
                                    <p><strong>年龄:</strong> 25岁</p>
                                    <p><strong>性别:</strong> 男</p>
                                    <p><strong>地址:</strong> 北京市朝阳区</p>
                                </div>`,
                        offset: new this.AMap.Pixel(0, -30),
                    });
                    infoWindow.open(this.map, feat.coordinates);

                }
            });

通过

 const feat = layer.queryFeature(e.pixel.toArray());
                console.log('feat', feat);

可以获取到当前点击的图层中的点的位置信息
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/739550.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Spark(19):SparkSQL中数据的加载和保存

目录 0. 相关文章链接 1. 通用的加载和保存方式 1.1. 加载数据 1.2. 保存数据 2. Parquet 2.1. 加载数据 2.2. 保存数据 3. JSON 4. CSV 5. MySQL 5.1. 导入依赖 5.2. 读取数据 5.3. 写入数据 6. Hive 6.1. SparkSQL连接Hive 6.2. 内嵌的 HIVE 6.3. 外部的 HI…

FastDFS文件系统

FastDFS文件系统 环境代码实现 一.FastDFS 1.什么是FastDFS FastDFS:Fast Distributed File System,快速的分布式文件系统,是一款用c语言开发的开源的分布式文件系统. FastDFS 是用 c 语言编写的一款开源的分布式文件系统。FastDFS为互联网量身定制&#xff0c;充分考虑了…

前端漏洞xss

网络钓鱼、获取Cookie、强制弹窗获取流量、网站挂马(将恶意代码嵌入程序&#xff0c;用户浏览页面时计算机将被嵌入木马)、发送垃圾信息或广告、传播蠕虫病毒 漏洞原理 XSS(Cross Site Scripting),是一种跨站的脚本攻击&#xff0c;曾简称为CSS&#xff0c; 后改为XSS。 攻击…

springboot在线考试

在线考试系统的开发运用java技术&#xff0c;MIS的总体思想&#xff0c;以及MYSQL等技术的支持下共同完成了该系统的开发&#xff0c;实现了在线考试管理的信息化&#xff0c;使用户体验到更优秀的在线考试管理&#xff0c;管理员管理操作将更加方便&#xff0c;实现目标.

Dell服务器的iDRAC管理卡连接

Dell服务器的iDRAC管理卡图文教程 1、网线连接idrac口2、查看idrac地址3、匹配IP地址4、web登录idrac页面5、登录成功页面 带有集成戴尔远程访问控制器 &#xff08;idrac&#xff09;的系统具有默认用户名和密码&#xff0c;但您也可以使用安全密码对其进行配置。默认使用web浏…

在idea中添加mapper.xml文件模板

文章目录 1、创建模板2. 创建mapper.xml文件 1、创建模板 打开创建模板页面 位置&#xff1a;File-》Settings-》Editor-》File and Code Templates 点击号&#xff0c;新建模板&#xff0c;填入Name&#xff08;随意&#xff09;、Extension&#xff08;必须为xml&#xff…

hadoop安装集群搭建

准备工作&#xff1a; 1&#xff0c;创建一台虚拟机&#xff0c;安装java jdk 这里选择1.8 2&#xff0c;给虚拟机并配置好静态IP地址 一&#xff1a;Java jdk安装 1.1检查安装环境&#xff0c;是否已安装其他版本的jdk&#xff0c;有的话卸载。 # 查看系统JDK进程 [rooth…

PSINS工具箱学习(二)姿态阵、四元数、欧拉角、等效旋转矢量的概念和转换

上一篇博客&#xff1a;PSINS工具箱学习&#xff08;一&#xff09;下载安装初始化、SINS-GPS组合导航仿真、习惯约定与常用变量符号、数据导入转换、绘图显示 文章目录 一、基础概念1、坐标系定义1. 惯性坐标系&#xff08; i 系 &#xff09;2. 地心地固坐标系&#xff08; e…

solr快速上手:搭建solr集群并创建核心,设置数据同步(十一)

0. 引言 前几章我们已经讲解了solr单机版的基本使用&#xff0c;但实际生产中&#xff0c;为了保证高可用、高性能&#xff0c;我们一般会采用集群模式&#xff0c;所以接下来&#xff0c;我们继续讲解solr集群的搭建和基本操作 1. 集群模式 1.1 分片 在讲解solr集群模式前…

【七天入门数据库】第二天 数据库理论基础

系列文章传送门&#xff1a; 【七天入门数据库】第一天 MySQL的安装部署 【七天入门数据库】第二天 数据库理论基础 【七天入门数据库】第三天 MySQL的库表操作 文章目录 一、什么是数据库 二、数据库管理系统DBMS 三、数据库与文件系统的区分 四、数据库的发展史 五、…

选择屏幕文本元素

标题 列标题 当你使用write语句时才能展现使用. 效果: 选择屏幕文本符号

前端Vue自定义tabs标题栏选项卡组件 可设置文字color

随着技术的发展&#xff0c;开发的复杂度也越来越高&#xff0c;传统开发方式将一个系统做成了整块应用&#xff0c;经常出现的情况就是一个小小的改动或者一个小功能的增加可能会引起整体逻辑的修改&#xff0c;造成牵一发而动全身。 通过组件化开发&#xff0c;可以有效实现…

7.10 qt作业

闹钟 #ifndef WIDGET_H #define WIDGET_H#include <QWidget> #include <QDebug> #include <QIcon> #include <QLabel> #include <QTextEdit> #include <QPushButton> #include <QLineEdit> #include <QPainter> #include <…

实现临界区互斥访问的基本方法

1. 问题引入 在我们之前的生产者与消费者问题中, 在文章的最后, 我们曾尝试过把我们的代码封装成P()和V()操作, 结果却以失败告终. 归根结底是因为我们无法在不使用mutex的情况下来完成对临界区的互斥访问, 本篇文章我们就来探讨一下, 如何不使用mutex实现临界区的互斥访问. …

形态学操作

目录 1、腐蚀 1.1 腐蚀目的 1.2 原理与代码实现 2、膨胀 3、应用 3.1 开闭运算、形态学梯度 3.1.1 开运算 3.1.2 闭运算 ​编辑 3.1.3 形态学梯度 ​编辑 3.1.4 顶帽与黑帽运算 3.2 相关函数 形态学操作常用于对二值化图像的操作 1、腐蚀 1.1 腐蚀目的 去除图像中…

[工业互联-22]:常见EtherCAT主站方案:Acontis公司的商用Windows 解决方案

目录 前言&#xff1a;非实时、纯软件解决方案 1.1 概述 1.2 缺点 1.3 实时性思路 方案1&#xff1a;非实时性能的解决方案&#xff1a;etherCAT优化网卡驱动程序 方案2&#xff1a;EtherCAT内核调度模块EcatDrv 方案3&#xff1a;具有硬实时性能的解决方案&#xff1a;…

【C++】C++创建动态链接库并调用

&#x1f60f;★,:.☆(&#xffe3;▽&#xffe3;)/$:.★ &#x1f60f; 这篇文章主要介绍。 学其所用&#xff0c;用其所学。——梁启超 欢迎来到我的博客&#xff0c;一起学习&#xff0c;共同进步。 喜欢的朋友可以关注一下&#xff0c;下次更新不迷路&#x1f95e; 文章目…

车载蓝牙通信开发之各种协议原理解析

车载蓝牙开发需要考虑到蓝牙协议栈集成、连接管理、电话功能集成、媒体播放控制、数据交换和服务发现、安全性和隐私保护等方面。这对于实现车辆与蓝牙设备之间的无线通信和交互功能非常关键。 使车辆能够与蓝牙设备进行通信和交互的开发过程。 蓝牙协议栈集成&#xff1a; …

SpringCloud与SpringBoot版本对应关系

浏览器访问start.spring.io/actuator/info 出现JSON字符串&#xff0c;再访问json.cn&#xff0c;在里面解析该字符串 如果对应不上&#xff0c;可能会出现很多环境上的坑

KKRT16 PSI算法

概念介绍 KKRT16 算法是一种基于OT的轻量级隐私求交协议&#xff0c;用于在半诚实敌手存在的情况下对伪随机函数&#xff08;OPRF&#xff09;进行不经意的评估。 在 OPRF 协议中&#xff0c;接收器有一个输入 r r r&#xff1b; 发送方获得输出 s s s&#xff0c;接收方获得…