heatmapjs 热力图
使用 leaflet 、heatmapjs 、vue 、加载热力图。
示例
安装依赖
官方示例仓库包存在问题 ImageData data assignment in Strict Mode
使用修改过的版本具体可查看仓库 issues
webpack 使用 npm 引入
shell
pnpm install leaflet-heatmap @mars3d/heatmap.js
vite 使用 script 引入
将 leaflet-heatmap.js 、heatmap.js
下载后在index.html 中引入猛击查看在线示例
js
<script src="./heatmap.js"></script>
<script src="./leaflet-heatmap.js"></script>
代码实现
vue
<script setup>
import { ref, defineAsyncComponent } from 'vue';
import { testData } from './testData.js';
import 'leaflet'
const InitMap = defineAsyncComponent(() =>
import('../../components/InitMapTianditu.vue')
);
const cfg = {
// 只有当scaleRadius为true时半径才应该很小(或者想要小半径)
// 如果scaleRadius为假,它将是以像素为单位使用的恒定半径
radius: 2,
maxOpacity: 0.8,
// 根据地图缩放比例缩放半径
scaleRadius: true,
// 如果设置为 false,热图将使用全局最大值进行着色
// 如果激活:使用当前地图边界内的数据最大值
// (useLocalExtremas 为 true 时总会出现红点)
useLocalExtrema: true,
// 数据中的哪个字段名称代表纬度 - 默认“lat”
latField: 'lat',
// 数据中的哪个字段名称代表经度 - 默认“lng”
lngField: 'lng',
// 数据中的哪个字段名称代表数据值 - 默认“值”
valueField: 'count'
};
const mapObj = ref();
const initHeatmap = () => {
mapObj.value.setView(new L.LatLng(25.6586, -80.3768), 4);
const heatmapLayer = new HeatmapOverlay(cfg);
mapObj.value.addLayer(heatmapLayer);
heatmapLayer.setData(testData);
};
/**
* todo 这样写只是因为是在 vitePress 中,项目中可直接在 script 中引入,需要注意的是 leaflet 要先于插件引入
*/
const setHeatmapjsToHead = () => {
const heatmapJs = document.createElement('script');
heatmapJs.src = '/heatmapjs/heatmap.js';
document.head.appendChild(heatmapJs);
const leafletHeatmapJs = document.createElement('script');
leafletHeatmapJs.src = '/heatmapjs/leaflet-heatmap.js';
document.head.appendChild(leafletHeatmapJs);
setTimeout(() => {
initHeatmap();
}, 1000);
};
const mapLoad = (map) => {
mapObj.value = map;
setHeatmapjsToHead();
};
</script>
<template>
<init-map @map-load="mapLoad"></init-map>
</template>
<style scoped></style>
vue
<script setup>
import { onMounted, onUnmounted, ref } from 'vue';
import L from 'leaflet';
const props = defineProps({
center: {
type: Array,
default: [32.0237855, 118.8075675]
}
})
const emit = defineEmits(['mapLoad']);
const mapRef = ref();
const initMap = () => {
const map = L.map(mapRef.value, {
center: props.center,
zoom: 11,
minZoom: 1,
maxZoom: 18
});
const mapType = 'vec';
L.tileLayer(
'https://t{s}.tianditu.gov.cn/' +
mapType +
'_w/wmts?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=' +
mapType +
'&STYLE=default&TILEMATRIXSET=w&FORMAT=tiles&TILECOL={x}&TILEROW={y}&TILEMATRIX={z}&tk=b72aa81ac2b3cae941d1eb213499e15e',
{
subdomains: ['0', '1', '2', '3', '4', '5', '6', '7'],
attribution:
'© <a href="http://lbs.tianditu.gov.cn/home.html">天地图 GS(2022)3124号 - 甲测资字1100471</a>'
}
).addTo(map);
const mapLabelType = 'cva';
L.tileLayer(
'https://t{s}.tianditu.gov.cn/' +
mapLabelType +
'_w/wmts?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=' +
mapLabelType +
'&STYLE=default&TILEMATRIXSET=w&FORMAT=tiles&TILECOL={x}&TILEROW={y}&TILEMATRIX={z}&tk=b72aa81ac2b3cae941d1eb213499e15e',
{
subdomains: ['0', '1', '2', '3', '4', '5', '6', '7']
}
).addTo(map);
// 地图初始化完成发送事件
emit('mapLoad', map);
return map;
};
const mapObj = ref();
// 在 onMounted 中初始化地图
onMounted(() => {
mapObj.value = initMap();
});
const removeMap = () => {
if (mapObj.value) {
mapObj.value.remove();
}
};
// 在组件卸载时删除地图
onUnmounted(() => {
removeMap();
});
</script>
<template>
<div ref="mapRef" class="map"></div>
</template>
<style scoped>
.map {
height: 40vh;
z-index: 0;
}
</style>
js
export const testData = {
max: 8,
data: [
{ lat: 24.6408, lng: 46.7728, count: 3 },
{ lat: 50.75, lng: -1.55, count: 1 },
{
lat: 52.6333,
lng: 1.75,
count: 1
},
{ lat: 48.15, lng: 9.4667, count: 1 },
{ lat: 52.35, lng: 4.9167, count: 2 },
{
lat: 60.8,
lng: 11.1,
count: 1
},
{ lat: 43.561, lng: -116.214, count: 1 },
{ lat: 47.5036, lng: -94.685, count: 1 },
{
lat: 42.1818,
lng: -71.1962,
count: 1
},
{ lat: 42.0477, lng: -74.1227, count: 1 },
{ lat: 40.0326, lng: -75.719, count: 1 },
{
lat: 40.7128,
lng: -73.2962,
count: 2
},
{ lat: 27.9003, lng: -82.3024, count: 1 },
{ lat: 38.2085, lng: -85.6918, count: 1 },
{
lat: 46.8159,
lng: -100.706,
count: 1
},
{ lat: 30.5449, lng: -90.8083, count: 1 },
{ lat: 44.735, lng: -89.61, count: 1 },
{
lat: 41.4201,
lng: -75.6485,
count: 2
},
{ lat: 39.4209, lng: -74.4977, count: 1 },
{ lat: 39.7437, lng: -104.979, count: 1 },
{
lat: 39.5593,
lng: -105.006,
count: 1
},
{ lat: 45.2673, lng: -93.0196, count: 1 },
{ lat: 41.1215, lng: -89.4635, count: 1 },
{
lat: 43.4314,
lng: -83.9784,
count: 1
},
{ lat: 43.7279, lng: -86.284, count: 1 },
{ lat: 40.7168, lng: -73.9861, count: 1 },
{
lat: 47.7294,
lng: -116.757,
count: 1
},
{ lat: 47.7294, lng: -116.757, count: 2 },
{ lat: 35.5498, lng: -118.917, count: 1 },
{
lat: 34.1568,
lng: -118.523,
count: 1
},
{ lat: 39.501, lng: -87.3919, count: 3 },
{ lat: 33.5586, lng: -112.095, count: 1 },
{
lat: 38.757,
lng: -77.1487,
count: 1
},
{ lat: 33.223, lng: -117.107, count: 1 },
{ lat: 30.2316, lng: -85.502, count: 1 },
{
lat: 39.1703,
lng: -75.5456,
count: 8
},
{ lat: 30.0041, lng: -95.2984, count: 2 },
{ lat: 29.7755, lng: -95.4152, count: 1 },
{
lat: 41.8014,
lng: -87.6005,
count: 1
},
{ lat: 37.8754, lng: -121.687, count: 7 },
{ lat: 38.4493, lng: -122.709, count: 1 },
{
lat: 40.5494,
lng: -89.6252,
count: 1
},
{ lat: 42.6105, lng: -71.2306, count: 1 },
{ lat: 40.0973, lng: -85.671, count: 1 },
{
lat: 40.3987,
lng: -86.8642,
count: 1
},
{ lat: 40.4224, lng: -86.8031, count: 4 },
{ lat: 47.2166, lng: -122.451, count: 1 },
{
lat: 32.2369,
lng: -110.956,
count: 1
},
{ lat: 41.3969, lng: -87.3274, count: 2 },
{ lat: 41.7364, lng: -89.7043, count: 2 },
{
lat: 42.3425,
lng: -71.0677,
count: 1
},
{ lat: 33.8042, lng: -83.8893, count: 1 },
{ lat: 36.6859, lng: -121.629, count: 2 },
{
lat: 41.0957,
lng: -80.5052,
count: 1
},
{ lat: 46.8841, lng: -123.995, count: 1 },
{ lat: 40.2851, lng: -75.9523, count: 2 },
{
lat: 42.4235,
lng: -85.3992,
count: 1
},
{ lat: 39.7437, lng: -104.979, count: 2 },
{ lat: 25.6586, lng: -80.3568, count: 7 },
{
lat: 33.0975,
lng: -80.1753,
count: 1
},
{ lat: 25.7615, lng: -80.2939, count: 1 },
{ lat: 26.3739, lng: -80.1468, count: 1 },
{
lat: 37.6454,
lng: -84.8171,
count: 1
},
{ lat: 34.2321, lng: -77.8835, count: 1 },
{ lat: 34.6774, lng: -82.928, count: 1 },
{
lat: 39.9744,
lng: -86.0779,
count: 1
},
{ lat: 35.6784, lng: -97.4944, count: 2 },
{ lat: 33.5547, lng: -84.1872, count: 1 },
{
lat: 27.2498,
lng: -80.3797,
count: 1
},
{ lat: 41.4789, lng: -81.6473, count: 1 },
{ lat: 41.813, lng: -87.7134, count: 1 },
{
lat: 41.8917,
lng: -87.9359,
count: 1
},
{ lat: 35.0911, lng: -89.651, count: 1 },
{ lat: 32.6102, lng: -117.03, count: 1 },
{
lat: 41.758,
lng: -72.7444,
count: 1
},
{ lat: 39.8062, lng: -86.1407, count: 1 },
{ lat: 41.872, lng: -88.1662, count: 1 },
{
lat: 34.1404,
lng: -81.3369,
count: 1
},
{ lat: 46.15, lng: -60.1667, count: 1 },
{ lat: 36.0679, lng: -86.7194, count: 1 },
{
lat: 43.45,
lng: -80.5,
count: 1
},
{ lat: 44.3833, lng: -79.7, count: 1 },
{ lat: 45.4167, lng: -75.7, count: 2 },
{
lat: 43.75,
lng: -79.2,
count: 2
},
{ lat: 45.2667, lng: -66.0667, count: 3 },
{ lat: 42.9833, lng: -81.25, count: 2 },
{
lat: 44.25,
lng: -79.4667,
count: 3
},
{ lat: 45.2667, lng: -66.0667, count: 2 },
{ lat: 34.3667, lng: -118.478, count: 3 },
{
lat: 42.734,
lng: -87.8211,
count: 1
},
{ lat: 39.9738, lng: -86.1765, count: 1 },
{ lat: 33.7438, lng: -117.866, count: 1 },
{
lat: 37.5741,
lng: -122.321,
count: 1
},
{ lat: 42.2843, lng: -85.2293, count: 1 },
{ lat: 34.6574, lng: -92.5295, count: 1 },
{
lat: 41.4881,
lng: -87.4424,
count: 1
},
{ lat: 25.72, lng: -80.2707, count: 1 },
{ lat: 34.5873, lng: -118.245, count: 1 },
{
lat: 35.8278,
lng: -78.6421,
count: 1
}
]
};
// const testData = {
// max: 8,
// data: [
// {
// lat: 32.020274,
// lng: 118.803319,
// count: 3
// },
// {
// lat: 32.020274,
// lng: 118.803319,
// count: 3
// },
// {
// lat: 32.015762,
// lng: 118.800572,
// count: 3
// },
// {
// lat: 32.015762,
// lng: 118.800572,
// count: 3
// },
// {
// lat: 32.015762,
// lng: 118.800572,
// count: 3
// },
// {
// lat: 32.013869,
// lng: 118.803834,
// count: 3
// },
// {
// lat: 32.013869,
// lng: 118.803834,
// count: 3
// },
// {
// lat: 32.013869,
// lng: 118.803834,
// count: 3
// }
// ]
// };
FAQ
vite 使用 heatmap.js 报错 "Cannot assign to read only property 'data' of object"
这个问题存在 heatmap.js' 原仓库,有许多这个问题的请求但没有被合并 猛击查看 ,有人发布了修改后的三方版本猛击查看 @mars3d/heatmap.js
webpack 正常引入使用无问题
js
import L from 'leaflet'
import 'leaflet/dist/leaflet.css'
import '@mars3d/heatmap.js';
import HeatmapOverlay from 'leaflet-heatmap';
vite 中会抛出上述错误
原因在于在 vite 中使用会执行 if (typeof module !== "undefined" && module.exports)
判断中的逻辑猛击查看 ,仍会去加载 heatmap.js
而不是 @mars3d/heatmap.js
。 解决办法:1. 按照如下方式修改 leaflet-heatmap.js
, 2. 按照上述 vite 使用将文件下载到本地在 index.html 中引入
js
// Supports UMD. AMD, CommonJS/Node.js and browser context
if (typeof module !== "undefined" && module.exports) {
module.exports = factory(
require('@mars3d/heatmap.js'),
require('leaflet')
);
} else if (typeof define === "function" && define.amd) {
define(['@mars3d/heatmap.js', 'leaflet'], factory);
} else {
// browser globals
if (typeof window.h337 === 'undefined') {
throw new Error('heatmap.js must be loaded before the leaflet heatmap plugin');
}
if (typeof window.L === 'undefined') {
throw new Error('Leaflet must be loaded before the leaflet heatmap plugin');
}
context[name] = factory(window.h337, window.L);
}