设计图
设计图涉及的功能点
- 需要通过下载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;
}
}