Cesium 热力图教程

热力图 · Heat Map · ▶ 在线运行案例

热力图

你将学到什么

  • 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

    小结

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值