前端离线地图 - 热力图

设计图

设计图涉及的功能点

  • 需要通过下载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;
     }
 }
相关推荐
nixiaoge17 分钟前
Web前端第二次作业
前端·javascript·css3
安冬的码畜日常22 分钟前
【玩转 Postman 接口测试与开发2_005】第六章:Postman 测试脚本的创建(上)
javascript·测试工具·单元测试·postman·bdd·chai
浮华似水29 分钟前
Docker入门系列——镜像原理
前端
Gavin_91531 分钟前
【JavaScript】数组-集合-Map-对象-Class用法一览
开发语言·前端·javascript
张保瑞1 小时前
十一:java web(3)-- Spring框架 -- Spring简介
java·前端·spring
墨柳烟1 小时前
ABAQUS高亮显示网格节点方法:Python为每个节点建立集合
开发语言·前端·python·abaqus
琴~~2 小时前
前端根据后端返回的文本流逐个展示文本内容
前端·javascript·vue
zhaocarbon2 小时前
el-scrollbar 动态更新内容 鼠标滚轮无效
前端·javascript·vue.js
一纸忘忧2 小时前
Nuxt 3.14 发布!全新功能与性能提升
前端·javascript·vue.js
少年姜太公2 小时前
【ES6】让你彻底搞懂const ,let和var的区别
javascript