前端离线地图 - 热力图

设计图

设计图涉及的功能点

  • 需要通过下载GeoJson数据来只显示自己想要显示地图模块
  • 绘制行政区区分虚线
  • 给每个行政区添加文字标识
  • 根据数据的区间来显示区块的颜色
  • 根据坐标点来绘制热力图

初始化地图

因为项目中可能有多个地方需要使用。我创建了一个class来初始化地图。代码如下

less 复制代码
 // 基础leaflet
 npm i leaflet
 // 需要下载相关包如下
 npm i leaflet-boundary-canvas  // 只显示边界
 ​
 npm i heatmap.js/plugins/leaflet-heatmap // 热力图
 ​
ini 复制代码
 import L from 'leaflet';
 import 'leaflet-boundary-canvas';
 import HeatmapOverlay from 'heatmap.js/plugins/leaflet-heatmap';
 ​
 export default class LMap {
     map: L.Map;
     dom: HTMLElement;
     url = '/roadmap/{z}/{x}/{y}.png';
     geojson?: any;
     nameTagLayer?: any;
     mapDefaultColor = 'rgba(171, 205, 255, 1)';
     textLayerGroup?: L.LayerGroup;
     baseLayer?: any;
     shiyanCode = [420303, 420302];
     constructor(dom: HTMLElement, opt?: L.MapOptions) {
       this.dom = dom;
       // 初始化地图
       this.map = L.map(dom, {
           ...{
                 center: [36.67, 105.17],
                 zoom: 4.4,
                 zoomControl: false,
                 attributionControl: false,
             },
             ...opt,
         });
     }
 }

代码解析

  • 创建一个自定义地图对象
  • 初始化一个map对象。具体option选项可以参考官网文档

保留特定的行政区块

通过上面的GeoJson网站来下载到GeoJson的json包之后。利用leaflet-boundary-canvas插件来绘制边界。

kotlin 复制代码
    drawBoundary(geojson) {
         this.geojson = geojson;
         this.baseLayer = L.TileLayer.boundaryCanvas(this.url, {
             boundary: geojson,
         }).addTo(this.map);
     }

代码解析

对行政区块做虚线区分

php 复制代码
    drawBoundaryLine(geojson?: any, style?: any) {
         // 绘制geojson的区域线
         return L.geoJSON(geojson || China, {
             ...{
                 stroke: true,
                 color: '#62F6E5',
                 weight: 1,
                 fillColor: '#053B39',
                 fillOpacity: 1,
                 dashArray: 10
             },
             ...style,
         }).addTo(this.map);
     }

代码解析

  • 对行政区的分界线进行自定义。具体可以参考svg的属性说明
  • 其中配置dashArray来绘制虚线
  • color属性为线的颜色
  • fillColor为行政区颜色区分

添加文字标识

ini 复制代码
     drawTextLayer(data: any, unit?: any) {
         // 文字
         const textLayer: any = [];
         this.map.eachLayer((layer: any) => {
             if (layer && layer.feature) {
                 const { adcode, center, name } = layer.feature.properties;
                 const item = data.find((item: any) => item.administrativeDivisionCode == adcode);
                 const myIcon = L.divIcon({
                     className: 'location-tag',
                     html: `<div class="province-title fs14 color-white text-center mapPointText">
                          
                           <div class="fs12 color-white">${this.shiyanCode.indexOf(adcode) > -1 ? '城区' : name}</div>
                            <div class="color-white fs12">(${item ? item.nums : 0}${unit || '人'})</div>
                           </div>`,
                     iconSize: [120, 20],
                 });
                 if (center && name) {
                     if (this.shiyanCode.indexOf(adcode) > -1) {
                         textLayer.push(L.marker([32.627576, 110.784317], { icon: myIcon }));
                     } else {
                         textLayer.push(L.marker([center[1], center[0]], { icon: myIcon }));
                     }
                 }
             }
         });
         this.textLayerGroup = L.layerGroup(textLayer).addTo(this.map);
     }

代码解析

  • 因为返回的数据为数组,所以我们需要将返回的数据的相关数据绑定至页面的行政区块。
  • 添加文字的原理就是在每个行政区的GeoJson的center中心点处添加一个DivIcon点击查看文档
  • 我们可以一个一个添加DivIcon,但是在需要清楚这些元素的时候会很麻烦。所以我们直接添加一个layerGroup图层。在我们需要移除的时候直接layer.remove()移除。

区块区分颜色显示

typescript 复制代码
     drawLayerFillColor(data: any, clear?: boolean) {
         // 区块上色
         this.map.eachLayer((layer: any) => {
             if (layer && layer.feature) {
                 const { adcode } = layer.feature.properties;
                 const item = data.find((item: any) => item.administrativeDivisionCode == adcode);
                 if (item) {
                     layer.setStyle({
                         fillColor: clear
                             ? this.mapDefaultColor
                             : marketStatus[item.rank]
                               ? marketStatus[item.rank].color
                               : this.mapDefaultColor,
                     });
                 } else {
                     layer.setStyle({
                         fillColor: this.mapDefaultColor,
                     });
                 }
             }
         });
     }

代码解析

  • 遍历由刚刚的GeoJson创建的layer然后给layer对象设定自定义颜色。其中需要注意的是layer为svg对象。可以使用svg的相关属性

绘制热力图

kotlin 复制代码
     drawHeatMap(data: any) {
         // 创建热力图的 Pane,并设置 zIndex 确保热力图位于顶层
         this.map.createPane('heatmapPane');
         (this.map.getPane('heatmapPane') as any).style.zIndex = 300;
         const cfg = {
             radius: 0.1, // 热力图半径
             maxOpacity: 1,
             scaleRadius: true,
             useLocalExtrema: true,
             latField: 'lat', // 自定义data字段
             lngField: 'lng', // 自定义data 字段
             valueField: 'count',
             pane: 'heatmapPane',
             // gradient: { // 热力颜色自定义
             //     0.1: 'rgba(13,251,198,0.5)',
             //     0.2: 'rgba(13,251,198,0.8)',
             //     0.3: 'rgba(255,241,46,0.5)',
             //     0.4: 'rgba(255,241,46,0.7)',
             //     0.5: 'rgba(255,241,46,0.8)',
             //     0.6: 'rgba(255,241,46,0.9)',
             //     0.7: 'rgba(255,170,0,0.5)',
             //     0.8: 'rgba(255,170,0,0.7)',
             //     0.9: 'rgba(255,170,0,0.9)',
             //     1.0: 'rgba(255,170,0,1)',
             // },
         };
 ​
         // 创建热力图图层
         this.heatmapLayer = new HeatmapOverlay(cfg); //  创建热力图实例
         this.heatmapLayer.addTo(this.map);     // 将热力图添加至地图
 ​
         const heatmapElement = this.heatmapLayer._heatmap._renderer.canvas;
         heatmapElement.style.zIndex = 9999; 
         const testData = {
             data,
         };
 ​
         this.heatmapLayer.setData(testData);
     }

代码解析

  • 这里创建一个pane用于渲染热力图。创建pane的原因是因为默认创建的热力图则层级会在默认默认创建的地图层级之下。故为了让它显示在地图之上。需要创建一个pane来自定义层级显示在地图上面。
  • 去看heatmapjs文档对相关属性进行个性化配置
  • 部分属性解释查看代码注释。

特殊注意

因为heatmap.js为非ts库。故在ts项目中会出现ts报错。只需要在项目src根目录创建heatmap.js.d.ts文件,文件内容如下

typescript 复制代码
 // src/heatmap.js.d.ts
 declare module 'heatmap.js/plugins/leaflet-heatmap' {
     import * as L from 'leaflet';
 ​
     interface HeatmapConfiguration {
         radius?: number;
         maxOpacity?: number;
         scaleRadius?: boolean;
         useLocalExtrema?: boolean;
         latField?: string;
         lngField?: string;
         valueField?: string;
         gradient?: { [key: number]: string };
         pane?: string;
     }
 ​
     interface HeatmapDataPoint {
         lat: number;
         lng: number;
         count: number;
     }
 ​
     interface HeatmapData {
         max?: number;
         data: HeatmapDataPoint[];
     }
 ​
     export default class HeatmapOverlay extends L.Layer {
         constructor(config: HeatmapConfiguration);
         setData(data: HeatmapData): void;
         addData(point: HeatmapDataPoint): void;
     }
 }
相关推荐
Domain-zhuo9 分钟前
什么是JavaScript原型链?
开发语言·前端·javascript·jvm·ecmascript·原型模式
小丁爱养花17 分钟前
前端三剑客(三):JavaScript
开发语言·前端·javascript
ZwaterZ25 分钟前
vue el-table表格点击某行触发事件&&操作栏点击和row-click冲突问题
前端·vue.js·elementui·c#·vue
码农六六25 分钟前
vue3封装Element Plus table表格组件
javascript·vue.js·elementui
西凉河的葛三叔30 分钟前
vue3+elementui-plus el-dialog全局配置点击空白处不关闭弹窗
前端·vue3·elementui-plus
徐同保30 分钟前
el-table 多选改成单选
javascript·vue.js·elementui
快乐小土豆~~30 分钟前
el-input绑定点击回车事件意外触发页面刷新
javascript·vue.js·elementui
周三有雨37 分钟前
【面试题系列Vue07】Vuex是什么?使用Vuex的好处有哪些?
前端·vue.js·面试·typescript
木古古181 小时前
使用chrome 访问虚拟机Apache2 的默认页面,出现了ERR_ADDRESS_UNREACHABLE这个鸟问题
前端·chrome·apache
爱米的前端小笔记1 小时前
前端八股自学笔记分享—页面布局(二)
前端·笔记·学习·面试·求职招聘