Heatmap Points
Runnable Cesium example: Heatmap Points. Open the live demo, read the complete source, and copy the file into your project. No build step or API key.
single HTML file · CDN dependencies onlyinterface may contain Chinese labels
Source
<!--
示例:用图标叠加做出热力效果
要点:canvas 生成径向渐变贴图,按到簇中心的距离算出权重,权重映射到颜色与尺寸
依赖:CesiumJS 1.121 · jsDelivr CDN,渐变贴图由 canvas 实时生成
-->
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta name="robots" content="noindex,follow">
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>热力图点</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/cesium@1.121.0/Build/Cesium/Widgets/widgets.css">
<style>
html,body{height:100%;margin:0}
body{background:#0e1116;color:#e6e8ec;overflow:hidden;
font:13px/1.6 -apple-system,"Segoe UI","PingFang SC",sans-serif}
#c{height:100%}
.hud{position:absolute;left:12px;top:12px;z-index:5;background:rgba(23,28,35,.9);
border:1px solid #2a313b;border-radius:6px;padding:9px 12px;font-size:12px;
color:#a2a9b6;max-width:250px}
.hud b{color:#e6e8ec;font-weight:600}
.hud .row{margin-top:6px}
.legend{height:8px;border-radius:4px;margin-top:6px;
background:linear-gradient(90deg,#4d9bff,#3fe0b0,#ffd666,#ff5c5c)}
.scale{display:flex;justify-content:space-between;font-size:11px;color:#7f8899}
input[type=range]{width:100%;margin-top:6px}
.cesium-viewer-bottom .cesium-widget-credits,.cesium-viewer-animationContainer,
.cesium-viewer-timelineContainer{display:none!important}
</style>
</head>
<body>
<div id="c"></div>
<div class="hud">
<div><b>热度分布</b></div>
<div>亮度按簇中心距离递减</div>
<div class="legend"></div>
<div class="scale"><span>低</span><span>高</span></div>
<div class="row"><input id="radius" type="range" min="0.3" max="2" step="0.1" value="1"></div>
</div>
<script src="https://cdn.jsdelivr.net/npm/cesium@1.121.0/Build/Cesium/Cesium.js"></script>
<script>
(function () {
'use strict';
var viewer = new Cesium.Viewer('c', {
baseLayer: false,
baseLayerPicker: false, geocoder: false, homeButton: false,
sceneModePicker: false, navigationHelpButton: false,
animation: false, timeline: false, fullscreenButton: false,
infoBox: false, selectionIndicator: false
});
var scene = viewer.scene;
scene.globe.baseColor = Cesium.Color.fromCssColorString('#1c2733');
scene.backgroundColor = Cesium.Color.fromCssColorString('#0e1116');
scene.skyBox.show = false;
scene.sun.show = false;
scene.moon.show = false;
if (scene.skyAtmosphere) scene.skyAtmosphere.show = false;
/* 径向渐变贴图:中心白、边缘全透明,叠起来就是热力光斑 */
function glowTexture() {
var size = 64;
var canvas = document.createElement('canvas');
canvas.width = canvas.height = size;
var ctx = canvas.getContext('2d');
var g = ctx.createRadialGradient(size / 2, size / 2, 1, size / 2, size / 2, size / 2);
g.addColorStop(0, 'rgba(255,255,255,0.95)');
g.addColorStop(0.45, 'rgba(255,255,255,0.35)');
g.addColorStop(1, 'rgba(255,255,255,0)');
ctx.fillStyle = g;
ctx.fillRect(0, 0, size, size);
return canvas;
}
var TEXTURE = glowTexture();
/* 三个簇:长三角一带,半径单位为度 */
var CLUSTERS = [
{ lng: 121.4737, lat: 31.2304, r: 0.30, weight: 0.55 },
{ lng: 120.5853, lat: 31.2989, r: 0.20, weight: 0.28 },
{ lng: 120.1551, lat: 30.2741, r: 0.18, weight: 0.17 }
];
var seed = 20261027;
function rand() {
seed = (seed * 1103515245 + 12345) & 0x7fffffff;
return seed / 0x7fffffff;
}
function gauss() {
var u = Math.max(rand(), 1e-6);
var v = rand();
return Math.sqrt(-2 * Math.log(u)) * Math.cos(2 * Math.PI * v);
}
var STOPS = [
[0.00, [77, 155, 255]],
[0.40, [63, 224, 176]],
[0.70, [255, 214, 102]],
[1.00, [255, 92, 92]]
];
function heatColor(w) {
for (var i = 1; i < STOPS.length; i++) {
if (w <= STOPS[i][0] || i === STOPS.length - 1) {
var a = STOPS[i - 1], b = STOPS[i];
var k = (w - a[0]) / (b[0] - a[0]);
var r = Math.round(a[1][0] + (b[1][0] - a[1][0]) * k);
var g = Math.round(a[1][1] + (b[1][1] - a[1][1]) * k);
var bl = Math.round(a[1][2] + (b[1][2] - a[1][2]) * k);
return Cesium.Color.fromBytes(r, g, bl, 255).withAlpha(0.55 + w * 0.35);
}
}
return Cesium.Color.WHITE;
}
var spots = [];
for (var n = 0; n < 260; n++) {
var pick = rand();
var acc = 0, cluster = CLUSTERS[0];
for (var c = 0; c < CLUSTERS.length; c++) {
acc += CLUSTERS[c].weight;
if (pick <= acc) { cluster = CLUSTERS[c]; break; }
}
var dist = Math.abs(gauss()) * cluster.r;
var angle = rand() * Math.PI * 2;
var lng = cluster.lng + Math.cos(angle) * dist;
var lat = cluster.lat + Math.sin(angle) * dist * 0.75;
var weight = Math.max(0, 1 - dist / (cluster.r * 1.9));
spots.push({ lng: lng, lat: lat, w: weight });
}
var baseScale = [];
spots.forEach(function (s) {
var scale = 0.35 + s.w * 1.15;
baseScale.push(scale);
viewer.entities.add({
position: Cesium.Cartesian3.fromDegrees(s.lng, s.lat, 0),
billboard: {
image: TEXTURE,
color: heatColor(s.w),
scale: scale,
disableDepthTestDistance: Number.POSITIVE_INFINITY
}
});
});
var entities = viewer.entities.values;
document.getElementById('radius').addEventListener('input', function () {
var k = parseFloat(this.value);
for (var i = 0; i < entities.length; i++) {
if (entities[i].billboard) entities[i].billboard.scale = baseScale[i] * k;
}
});
viewer.camera.flyTo({
destination: Cesium.Cartesian3.fromDegrees(120.98, 30.98, 210000),
orientation: { heading: 0, pitch: Cesium.Math.toRadians(-82), roll: 0 },
duration: 0
});
})();
</script>
</body>
</html>
Language: the code and this page are in English; the full tutorial and API notes for this example are in Chinese. The demo itself is the authoritative version — it runs exactly as shown.