热力图 · Heat Map · ▶ 在线运行案例
案例合集: 三维可视化功能案例(threehub.cn)
开源仓库github地址: https://github.com/z2586300277/three-cesium-examples
**400个案例代码: ** 网盘链接

你将学到什么
- Cesium Viewer 初始化
- Cesium Entity 高层 API
- Cesium 影像图层
- Cesium 动态材质属性
- 热力图可视化
效果说明
热力或密度可视化,通常把数值映射到颜色/高度。入口在 setCesiumHeatmap。
核心概念
Viewer 封装地球、相机、图层;可关闭 animation/timeline 等 UI 精简界面。
Entity 加点线面、模型、标签;适合业务对象与交互。
ImageryLayer 叠加 XYZ/WMTS/ArcGIS 等底图,
imageryLayers.add/remove管理。MaterialProperty 支持 CallbackProperty 时间动态材质。
实现步骤
- 初始化
Cesium.Viewer与底图图层 - 添加 Entity / Primitive / DataSource 等业务对象
- 按需
camera.flyTo定位视角
代码要点
import {
EllipsoidSurfaceAppearance,
GeometryInstance,
Material,
Primitive,
Rectangle,
RectangleGeometry,
SingleTileImageryProvider,
ImageryLayer,
ImageMaterialProperty,
Entity
} from "cesium";
import * as Cesium from "cesium";
/* ----------------------------------------------------heatMap类----------------------------------------------- */
var HeatmapConfig = {
defaultRadius: 40,
defaultRenderer: "canvas2d",
defaultGradient: {
0.25: "rgb(0,0,255)",
0.55: "rgb(0,255,0)",
0.85: "yellow",
1.0: "rgb(255,0,0)",
},
defaultMaxOpacity: 1,
defaultMinOpacity: 0,
defaultBlur: 0.85,
defaultXField: "x",
defaultYField: "y",
defaultValueField: "value",
plugins: {},
};
var Store = (function StoreClosure() {
var Store = function Store(config) {
this._coordinator = {};
this._data = [];
this._radi = [];
this._min = 0;
this._max = 1;
this._xField = config["xField"] || config.defaultXField;
this._yField = config["yField"] || config.defaultYField;
this._valueField = config["valueField"] || config.defaultValueField;
if (config["radius"]) {
this._cfgRadius = config["radius"];
}
};
var defaultRadius = HeatmapConfig.defaultRadius;
Store.prototype = {
// when forceRender = false -> called from setData, omits renderall event
_organiseData: function (dataPoint, forceRender) {
var x = dataPoint[this._xField];
var y = dataPoint[this._yField];
var radi = this._radi;
var store = this._data;
var max = this._max;
var min = this._min;
var value = dataPoint[this._valueField] || 1;
var radius = dataPoint.radius || this._cfgRadius || defaultRadius;
if (!store[x]) {
store[x] = [];
radi[x] = [];
}
if (!store[x][y]) {
store[x][y] = value;
radi[x][y] = radius;
} else {
store[x][y] += value;
}
if (store[x][y] > max) {
if (!forceRender) {
this._max = store[x][y];
} else {
this.setDataMax(store[x][y]);
}
return false;
} else {
return {
x: x,
y: y,
value: value,
radius: radius,
min: min,
max: max,
};
}
},
_unOrganizeData: function () {
var unorganizedData = [];
var data = this._data;
var radi = this._radi;
for (var x in data) {
for (var y in data[x]) {
unorganizedData.push({
x: x,
y: y,
radius: radi[x][y],
value: data[x][y],
});
}
}
return {
min: this._min,
max: this._max,
data: unorganizedData,
};
},
_onExtremaChange: function () {
this._coordinator.emit("extremachange", {
min: this._min,
max: this._max,
});
},
addData: function () {
if (arguments[0].length > 0) {
var dataArr = arguments[0];
var dataLen = dataArr.length;
while (dataLen--) {
this.addData.call(this, dataArr[dataLen]);
}
} else {
// add to store
var organisedEntry = this._organiseData(arguments[0], true);
if (organisedEntry) {
this._coordinator.emit("renderpartial", {
min: this._min,
max: this._max,
data: [organisedEntry],
});
}
}
return this;
},
setData: function (data) {
var dataPoints = data.data;
var pointsLen = dataPoints.length;
// reset data arrays
this._data = [];
this._radi = [];
for (var i = 0; i < pointsLen; i++) {
this._organiseData(dataPoints[i], false);
}
this._max = data.max;
this._min = data.min || 0;
this._onExtremaChange();
this._coordinator.emit("renderall", this._getInternalData());
return this;
},
removeData: function () {
// TODO: implement
},
setDataMax: function (max) {
this._max = max;
this._onExtremaChange();
this._coordinator.emit("renderall", this._getInternalData());
return this;
},
setDataMin: function (min) {
this._min = min;
this._onExtremaChange();
this._coordinator.emit("renderall", this._getInternalData());
return this;
},
setCoordinator: function (coordinator) {
this._coordinator = coordinator;
},
_getInternalData: function () {
return {
max: this._max,
min: this._min,
data: this._data,
radi: this._radi,
};
},
getData: function () {
return this._unOrganizeData();
} /*,
TODO: rethink.
getValueAt: function(point) {
var value;
var radius = 100;
var x = point.x;
var y = point.y;
var data = this._data;
if (data[x] && data[x][y]) {
return data[x][y];
} else {
var values = [];
// radial search for datapoints based on default radius
for(var distance = 1; distance < radius; distance++) {
var neighbors = distance * 2 +1;
var startX = x - distance;
var startY = y - distance;
for(var i = 0; i < neighbors; i++) {
for (var o = 0; o < neighbors; o++) {
if ((i == 0 || i == neighbors-1) || (o == 0 || o == neighbors-1)) {
if (data[startY+i] && data[startY+i][startX+o]) {
values.push(data[startY+i][startX+o]);
}
} else {
continue;
}
}
}
}
if (values.length > 0) {
return Math.max.apply(Math, values);
}
}
return false;
}*/,
};
return Store;
})();
var Canvas2dRenderer = (function Canvas2dRendererClosure() {
var _getColorPalette = function (config) {
var gradientConfig = config.gradient || config.defaultGradient;
var paletteCanvas = document.createElement("canvas");
var paletteCtx = paletteCanvas.getContext("2d");
paletteCanvas.width = 256;
paletteCanvas.height = 1;
var gradient = paletteCtx.createLinearGradient(0, 0, 256, 1);
for (var key in gradientConfig) {
gradient.addColorStop(key, gradientConfig[key]);
}
paletteCtx.fillStyle = gradient;
paletteCtx.fillRect(0, 0, 256, 1);
return paletteCtx.getImageData(0, 0, 256, 1).data;
};
var _getPointTemplate = function (radius, blurFactor) {
var tplCanvas = document.createElement("canvas");
var tplCtx = tplCanvas.getContext("2d");
var x = radius;
var y = radius;
tplCanvas.width = tplCanvas.height = radius * 2;
if (blurFactor == 1) {
tplCtx.beginPath();
tplCtx.arc(x, y, radius, 0, 2 * Math.PI, false);
tplCtx.fillStyle = "rgba(0,0,0,1)";
tplCtx.fill();
} else {
var gradient = tplCtx.createRadialGradient(
x,
y,
radius * blurFactor,
x,
y,
radius
);
gradient.addColorStop(0, "rgba(0,0,0,1)");
gradient.addColorStop(1, "rgba(0,0,0,0)");
tplCtx.fillStyle = gradient;
tplCtx.fillRect(0, 0, 2 * radius, 2 * radius);
}
return tplCanvas;
};
var _prepareData = function (data) {
var renderData = [];
var min = data.min;
var max = data.max;
var radi = data.radi;
var data = data.data;
var xValues = Object.keys(data);
var xValuesLen = xValues.length;
while (xValuesLen--) {
var xValue = xValues[xValuesLen];
var yValues = Object.keys(data[xValue]);
var yValuesLen = yValues.length;
while (yValuesLen--) {
var yValue = yValues[yValuesLen];
var value = data[xValue][yValue];
var radius = radi[xValue][yValue];
renderData.push({
x: xValue,
y: yValue,
value: value,
radius: radius,
});
}
}
return {
min: min,
max: max,
data: renderData,
};
};
function Canvas2dRenderer(config) {
var container = config.container;
var shadowCanvas = (this.shadowCanvas = document.createElement("canvas"));
var canvas = (this.canvas =
config.canvas || document.createElement("canvas"));
var renderBoundaries = (this._renderBoundaries = [10000, 10000, 0, 0]);
var computed = getComputedStyle(config.container) || {};
canvas.className = "heatmap-canvas";
this._width =
canvas.width =
shadowCanvas.width =
+computed.width.replace(/px/, "");
this._height =
canvas.height =
shadowCanvas.height =
+computed.height.replace(/px/, "");
this.shadowCtx = shadowCanvas.getContext("2d");
this.ctx = canvas.getContext("2d");
// @TODO:
// conditional wrapper
canvas.style.cssText = shadowCanvas.style.cssText =
"position:absolute;left:0;top:0;";
container.style.position = "relative";
container.appendChild(canvas);
this._palette = _getColorPalette(config);
this._templates = {};
this._setStyles(config);
}
Canvas2dRenderer.prototype = {
renderPartial: function (data) {
this._drawAlpha(data);
this._colorize();
},
renderAll: function (data) {
// reset render boundaries
this._clear();
this._drawAlpha(_prepareData(data));
this._colorize();
},
_updateGradient: function (config) {
this._palette = _getColorPalette(config);
},
updateConfig: function (config) {
if (config["gradient"]) {
this._updateGradient(config);
}
this._setStyles(config);
},
setDimensions: function (width, height) {
this._width = width;
this._height = height;
this.canvas.width = this.shadowCanvas.width = width;
this.canvas.height = this.shadowCanvas.height = height;
},
_clear: function () {
this.shadowCtx.clearRect(0, 0, this._width, this._height);
this.ctx.clearRect(0, 0, this._width, this._height);
},
_setStyles: function (config) {
this._blur = config.blur == 0 ? 0 : config.blur || config.defaultBlur;
if (config.backgroundColor) {
this.canvas.style.backgroundColor = config.backgroundColor;
}
this._opacity = (config.opacity || 0) * 255;
this._maxOpacity = (config.maxOpacity || config.defaultMaxOpacity) * 255;
this._minOpacity = (config.minOpacity || config.defaultMinOpacity) * 255;
this._useGradientOpacity = !!config.useGradientOpacity;
},
_drawAlpha: function (data) {
var min = (this._min = data.min);
var max = (this._max = data.max);
var data = data.data || [];
var dataLen = data.length;
// on a point basis?
var blur = 1 - this._blur;
while (dataLen--) {
var point = data[dataLen];
var x = point.x;
var y = point.y;
var radius = point.radius;
// if value is bigger than max
// use max as value
var value = Math.min(point.value, max);
var rectX = x - radius;
var rectY = y - radius;
var shadowCtx = this.shadowCtx;
var tpl;
if (!this._templates[radius]) {
this._templates[radius] = tpl = _getPointTemplate(radius, blur);
} else {
tpl = this._templates[radius];
}
// value from minimum / value range
// => [0, 1]
shadowCtx.globalAlpha = (value - min) / (max - min);
shadowCtx.drawImage(tpl, rectX, rectY);
// update renderBoundaries
if (rectX < this._renderBoundaries[0]) {
this._renderBoundaries[0] = rectX;
}
if (rectY < this._renderBoundaries[1]) {
this._renderBoundaries[1] = rectY;
}
if (rectX + 2 * radius > this._renderBoundaries[2]) {
this._renderBoundaries[2] = rectX + 2 * radius;
}
if (rectY + 2 * radius > this._renderBoundaries[3]) {
this._renderBoundaries[3] = rectY + 2 * radius;
}
}
},
_colorize: function () {
var x = this._renderBoundaries[0];
var y = this._renderBoundaries[1];
var width = this._renderBoundaries[2] - x;
var height = this._renderBoundaries[3] - y;
var maxWidth = this._width;
var maxHeight = this._height;
var opacity = this._opacity;
var maxOpacity = this._maxOpacity;
var minOpacity = this._minOpacity;
var useGradientOpacity = this._useGradientOpacity;
if (x < 0) {
x = 0;
}
if (y < 0) {
y = 0;
}
if (x + width > maxWidth) {
width = maxWidth - x;
}
if (y + height > maxHeight) {
height = maxHeight - y;
}
var img = this.shadowCtx.getImageData(x, y, width, height);
var imgData = img.data;
var len = imgData.length;
var palette = this._palette;
for (var i = 3; i < len; i += 4) {
var alpha = imgData[i];
var offset = alpha * 4;
if (!offset) {
continue;
}
var finalAlpha;
if (opacity > 0) {
finalAlpha = opacity;
} else {
if (alpha < maxOpacity) {
if (alpha < minOpacity) {
finalAlpha = minOpacity;
} else {
finalAlpha = alpha;
}
} else {
finalAlpha = maxOpacity;
}
}
imgData[i - 3] = palette[offset];
imgData[i - 2] = palette[offset + 1];
imgData[i - 1] = palette[offset + 2];
imgData[i] = useGradientOpacity ? palette[offset + 3] : finalAlpha;
}
Object.defineProperty(img, "data", {
value: imgData,
writable: true,
configurable: true,
enumerable: true,
});
// img.data = imgData;
this.ctx.putImageData(img, x, y);
this._renderBoundaries = [1000, 1000, 0, 0];
},
getValueAt: function (point) {
var value;
var shadowCtx = this.shadowCtx;
var img = shadowCtx.getImageData(point.x, point.y, 1, 1);
var data = img.data[3];
var max = this._max;
var min = this._min;
value = (Math.abs(max - min) * (data / 255)) >> 0;
return value;
},
getDataURL: function () {
return this.canvas.toDataURL();
},
};
return Canvas2dRenderer;
})();
var Renderer = (function RendererClosure() {
var rendererFn = false;
if (HeatmapConfig["defaultRenderer"] === "canvas2d") {
rendererFn = Canvas2dRenderer;
}
return rendererFn;
})();
var Util = {
merge: function () {
var merged = {};
var argsLen = arguments.length;
for (var i = 0; i < argsLen; i++) {
var obj = arguments[i];
for (var key in obj) {
merged[key] = obj[key];
}
}
return merged;
},
};
// Heatmap Constructor
var Heatmap = (function HeatmapClosure() {
var Coordinator = (function CoordinatorClosure() {
function Coordinator() {
this.cStore = {};
}
Coordinator.prototype = {
on: function (evtName, callback, scope) {
var cStore = this.cStore;
if (!cStore[evtName]) {
cStore[evtName] = [];
}
cStore[evtName].push(function (data) {
return callback.call(scope, data);
});
},
emit: function (evtName, data) {
var cStore = this.cStore;
if (cStore[evtName]) {
var len = cStore[evtName].length;
for (var i = 0; i < len; i++) {
var callback = cStore[evtName][i];
callback(data);
}
}
},
};
return Coordinator;
})();
var _connect = function (scope) {
var renderer = scope._renderer;
var coordinator = scope._coordinator;
var store = scope._store;
coordinator.on("renderpartial", renderer.renderPartial, renderer);
coordinator.on("renderall", renderer.renderAll, renderer);
coordinator.on("extremachange", function (data) {
scope._config.onExtremaChange &&
scope._config.onExtremaChange({
min: data.min,
max: data.max,
gradient:
scope._config["gradient"] || scope._config["defaultGradient"],
});
});
store.setCoordinator(coordinator);
};
function Heatmap() {
var config = (this._config = Util.merge(HeatmapConfig, arguments[0] || {}));
this._coordinator = new Coordinator();
if (config["plugin"]) {
var pluginToLoad = config["plugin"];
if (!HeatmapConfig.plugins[pluginToLoad]) {
throw new Error(
"Plugin '" +
pluginToLoad +
"' not found. Maybe it was not registered."
);
} else {
var plugin = HeatmapConfig.plugins[pluginToLoad];
// set plugin renderer and store
this._renderer = new plugin.renderer(config);
this._store = new plugin.store(config);
}
} else {
this._renderer = new Renderer(config);
this._store = new Store(config);
}
_connect(this);
}
// @TODO:
// add API documentation
Heatmap.prototype = {
addData: function () {
this._store.addData.apply(this._store, arguments);
return this;
},
removeData: function () {
this._store.removeData &&
this._store.removeData.apply(this._store, arguments);
return this;
},
setData: function () {
this._store.setData.apply(this._store, arguments);
return this;
},
setDataMax: function () {
this._store.setDataMax.apply(this._store, arguments);
return this;
},
setDataMin: function () {
this._store.setDataMin.apply(this._store, arguments);
return this;
},
configure: function (config) {
this._config = Util.merge(this._config, config);
this._renderer.updateConfig(this._config);
this._coordinator.emit("renderall", this._store._getInternalData());
return this;
},
repaint: function () {
this._coordinator.emit("renderall", this._store._getInternalData());
return this;
},
getData: function () {
return this._store.getData();
},
getDataURL: function () {
return this._renderer.getDataURL();
},
getValueAt: function (point) {
if (this._store.getValueAt) {
return this._store.getValueAt(point);
} else if (this._renderer.getValueAt) {
return this._renderer.getValueAt(point);
} else {
return null;
}
},
};
return Heatmap;
})();
var h337 = {
create: function (config) {
return new Heatmap(config);
},
register: function (pluginKey, plugin) {
HeatmapConfig.plugins[pluginKey] = plugin;
},
};
/* ----------------------------------------------------heatMap类--------------------------------------------------- */
class setCesiumHeatmap {
constructor(viewer, options) {
this.viewer = viewer;
this.initOptions = { ...options };
if (this.initOptions?.points) {
const bounds = this.getBounds(this.initOptions.points);
this.bounds = bounds;
const { container, width, height } = this.createContainer(bounds);
this.element = container;
const datas = [];
const values = [];
for (let i in this.initOptions.points) {
const point = this.initOptions.points[i];
const x = ((point.x - bounds[0]) / (bounds[2] - bounds[0])) * width; //屏幕坐标x
const y = ((bounds[3] - point.y) / (bounds[3] - bounds[1])) * height; //屏幕坐标y
const dataPoint = {
x: x,
y: y,
value: point.value,
};
if (typeof point.value === "number") values.push(point.value);
datas.push(dataPoint);
}
//数据的最大值和最小值
let _min = Math.min(...values),
_max = Math.max(...values);
if (this.initOptions?.heatmapDataOptions) {
const { min, max } = this.initOptions.heatmapDataOptions;
if (typeof min === "number") {
_min = min;
}
if (typeof max === "number") {
_max = max;
}
}
this.heatmapDataOptions = { min: _min, max: _max };
const data = {
max: _max,
min: _min,
data: datas,
};
const defaultOptions = {
maxOpacity: 0.9,
// radius: minRadius,
// minimum opacity. any value > 0 will produce
// no transparent gradient transition
minOpacity: 0.1,
gradient: {
// enter n keys between 0 and 1 here
// for gradient color customization
".3": "blue",
".5": "green",
".7": "yellow",
".95": "red",
},
};
const _options = this.initOptions.heatmapOptions
? { ...defaultOptions, ...this.initOptions.heatmapOptions }
: defaultOptions;
//初始化半径
if (this.heatmapOptions?.radius) {
this.initRadius = this.heatmapOptions.radius;
}
this.heatmapOptions = { ..._options };
const options = {
..._options,
container,
};
this.heatmap = h337.create(options);
this.heatmap.setData(data);
this.createLayer();
if (!this.initOptions.noLisenerCamera) {
this.addLisener();
}
if (this.initOptions.zoomToLayer && bounds) {
this.viewer.camera.flyTo({
destination: Rectangle.fromDegrees(...bounds),
});
}
if (this.initOptions?.viewToLayer) {
this.viewer.camera.setView({
destination: Rectangle.fromDegrees(...bounds),
});
}
}
}
/**
* 设置数据的最大最小值
* @param dataOption
*/
updateHeatMapMaxMin(dataOption) {
const { min, max } = dataOption;
if (this.heatmap) {
if (typeof min === "number") {
this.heatmap.setDataMin(min);
if (this.heatmapDataOptions) this.heatmapDataOptions.min = min;
}
if (typeof max === "number") {
this.heatmap.setDataMax(max);
if (this.heatmapDataOptions) this.heatmapDataOptions.max = max;
}
}
this.updateLayer();
}
/**
* 更新热度图配置
* @param options
*/
updateHeatmap(options) {
const { heatmapOptions } = this;
this.heatmap.configure({ ...heatmapOptions, ...options });
this.updateLayer();
}
/**
* 更新半径
* @param radius
*/
updateRadius(radius) {
const { heatmapOptions } = this;
const currentData = this.heatmap.getData();
if (currentData?.data) {
for (let i in currentData.data) {
const data = currentData.data[i];
data.radius = radius;
}
}
this.heatmap.setData(currentData);
this.heatmapOptions = { ...heatmapOptions, ...{ radius } };
this.updateLayer();
if (this.initOptions?.onRadiusChange) {
this.initOptions.onRadiusChange(radius);
}
}
/**
* 移除
*/
remove() {
if (this.element) {
document.body.removeChild(this.element);
this.element = undefined;
if (this.provider instanceof ImageryLayer) {
if (this.provider) this.viewer.imageryLayers.remove(this.provider);
this.createSingleTileImageryLayer();
} else if (this.provider instanceof Primitive) {
this.viewer.scene.primitives.remove(this.provider);
} else if (this.provider instanceof Entity) {
this.viewer.entities.remove(this.provider);
}
if (this.cameraMoveEnd) {
this.viewer.camera.moveEnd.removeEventListener(this.cameraMoveEnd);
this.cameraMoveEnd = undefined;
}
}
}
createLayer() {
if (this.initOptions.renderType === "primitive") {
this.createPrimitive();
} else if (this.initOptions.renderType === "imagery") {
this.createSingleTileImageryLayer();
} else {
this.createEntity();
}
}
createPrimitive() {
const url = this.heatmap.getDataURL();
this.provider = this.viewer.scene.primitives.add(
new Primitive({
geometryInstances: new GeometryInstance({
geometry: new RectangleGeometry({
rectangle: Rectangle.fromDegrees(...this.bounds),
vertexFormat: EllipsoidSurfaceAppearance.VERTEX_FORMAT,
}),
}),
appearance: new EllipsoidSurfaceAppearance({
aboveGround: false,
}),
show: true,
})
);
if (this.provider) {
this.provider.appearance.material = new Material({
fabric: {
type: "Image",
uniforms: {
image: url,
},
},
});
}
}
createSingleTileImageryLayer() {
const url = this.heatmap.getDataURL();
this.provider = this.viewer.imageryLayers.addImageryProvider(
new SingleTileImageryProvider({
url: url,
rectangle: Rectangle.fromDegrees(...this.bounds),
})
);
}
getImageMaterialProperty() {
const url = this.heatmap.getDataURL();
const material = new ImageMaterialProperty({
image: url,
});
return material;
}
createEntity() {
this.provider = this.viewer.entities.add({
show: true,
rectangle: {
coordinates: Rectangle.fromDegrees(...this.bounds),
material: this.getImageMaterialProperty(),
},
});
}
updateLayer() {
const src = this.heatmap.getDataURL();
if (this.provider instanceof ImageryLayer) {
if (this.provider) this.viewer.imageryLayers.remove(this.provider);
this.createSingleTileImageryLayer();
} else if (this.provider instanceof Primitive) {
this.provider.appearance.material.uniforms.image = src;
} else if (this.provider instanceof Entity) {
if (this.provider.rectangle)
this.provider.rectangle.material = this.getImageMaterialProperty();
}
}
/**
* 添加相机的监听
*/
addLisener() {
const maxRadius = 100;
const min = 6375000;
const max = 10000000;
this.cameraMoveEnd = () => {
if (this.heatmapOptions && this.heatmap && this.heatmapDataOptions) {
const h = this.viewer.camera.getMagnitude();
const distance = this?.initOptions?.cameraHeightDistance
? this.initOptions.cameraHeightDistance
: 1000;
if (Math.abs(h - this.lastCameraHeight) > distance) {
this.lastCameraHeight = h;
if (typeof min === "number" && typeof max === "number") {
const radius = parseInt(
(
this.initRadius +
((maxRadius - this.initRadius) * (h - min)) / (max - min)
).toFixed(0)
);
if (radius) {
this.updateRadius(radius);
}
}
}
}
};
this.viewer.camera.moveEnd.addEventListener(this.cameraMoveEnd);
}
/**
*
* @param points
* @param expand
* @returns
*/
getBounds(points) {
if (points) {
let lonMin = 180;
let lonMax = -180;
let latMin = 90;
let latMax = -180;
points.forEach(function (point) {
const { x: longitude, y: latitude } = point;
lonMin = longitude < lonMin ? longitude : lonMin;
latMin = latitude < latMin ? latitude : latMin;
lonMax = longitude > lonMax ? longitude : lonMax;
latMax = latitude > latMax ? latitude : latMax;
});
const xRange = lonMax - lonMin ? lonMax - lonMin : 1;
const yRange = latMax - latMin ? latMax - latMin : 1;
return [
lonMin - xRange / 10,
latMin - yRange / 10,
lonMax + xRange / 10,
latMax + yRange / 10,
];
}
return [0, 0, 0, 0];
}
createContainer(bounds) {
const container = document.createElement("div");
const width = 1000;
const height = parseInt(
((1000 / (bounds[2] - bounds[0])) * (bounds[3] - bounds[1])).toFixed(0)
);
container.setAttribute(
"style",
`width:${width}px;height:${height}px;display:none;`
);
document.body.appendChild(container);
return { container, width, height };
}
}
const DOM = document.getElementById('box')
const viewer = new Cesium.Viewer(DOM, {
animation: false,//是否创建动画小器件,左下角仪表
baseLayerPicker: false,//是否显示图层选择器,右上角图层选择按钮
baseLayer: Cesium.ImageryLayer.fromProviderAsync(Cesium.ArcGisMapServerImageryProvider.fromUrl('https://server.arcgisonline.com/arcgis/rest/services/World_Imagery/MapServer')),
fullscreenButton: false,//是否显示全屏按钮,右下角全屏选择按钮
timeline: false,//是否显示时间轴
infoBox: false,//是否显示信息框
})
viewer._cesiumWidget._creditContainer.style.display = "none"
// 模拟数值
const points = new Array(50).fill('').map(() => {
return {
x: 116.46 + Math.random() * 10,
y: 39.92 + Math.random() * 10,
value: Math.random()
}
})
// 创建热力图
new setCesiumHeatmap(viewer, {
points,
viewToLayer: true,
heatmapDataOptions: { max: 1, min: 0 },
heatmapOptions: {
radius: 100,
maxOpacity: 0.5,
minOpacity: 0
}
})
完整源码:GitHub
小结
- 本文提供 热力图 完整 Cesium.js 源码与在线 Demo,建议先运行案例再改 uniform/参数做二次实验
- 更多 Cesium.js 实战案例见 three-cesium-examples 合集 与 GitHub 开源仓库