mirror of
				https://github.com/jiawanlong/Cesium-Examples.git
				synced 2025-11-04 09:14:17 +00:00 
			
		
		
		
	
		
			
	
	
		
			1195 lines
		
	
	
		
			45 KiB
		
	
	
	
		
			HTML
		
	
	
	
	
	
		
		
			
		
	
	
			1195 lines
		
	
	
		
			45 KiB
		
	
	
	
		
			HTML
		
	
	
	
	
	
| 
								 | 
							
								<!doctype html>
							 | 
						|||
| 
								 | 
							
								<html lang="en">
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								<!-- 
							 | 
						|||
| 
								 | 
							
								* 名称: 3D热力图
							 | 
						|||
| 
								 | 
							
								* 作者: 20Savage https://github.com/zhengsixsix
							 | 
						|||
| 
								 | 
							
								 -->
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								<head>
							 | 
						|||
| 
								 | 
							
								    <meta charset="UTF-8" />
							 | 
						|||
| 
								 | 
							
								    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
							 | 
						|||
| 
								 | 
							
								    <title>3D热力图</title>
							 | 
						|||
| 
								 | 
							
								    <link rel="stylesheet" href="https://file.threehub.cn/js/cesium/style.css">
							 | 
						|||
| 
								 | 
							
								    <script type="importmap">
							 | 
						|||
| 
								 | 
							
								        {
							 | 
						|||
| 
								 | 
							
								            "imports": {
							 | 
						|||
| 
								 | 
							
								                "cesium": "https://file.threehub.cn/js/cesium/Cesium.js"
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								    </script>
							 | 
						|||
| 
								 | 
							
								    <style>
							 | 
						|||
| 
								 | 
							
								        body {
							 | 
						|||
| 
								 | 
							
								            margin: 0;
							 | 
						|||
| 
								 | 
							
								            padding: 1px;
							 | 
						|||
| 
								 | 
							
								            box-sizing: border-box;
							 | 
						|||
| 
								 | 
							
								            background-color: #1f1f1f;
							 | 
						|||
| 
								 | 
							
								            display: flex;
							 | 
						|||
| 
								 | 
							
								            flex-direction: column;
							 | 
						|||
| 
								 | 
							
								            width: 100vw;
							 | 
						|||
| 
								 | 
							
								            height: 100vh;
							 | 
						|||
| 
								 | 
							
								            overflow: hidden;
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        #box {
							 | 
						|||
| 
								 | 
							
								            width: 100%;
							 | 
						|||
| 
								 | 
							
								            height: 100%;
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								    </style>
							 | 
						|||
| 
								 | 
							
								</head>
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								<body>
							 | 
						|||
| 
								 | 
							
								    <div id="box"></div>
							 | 
						|||
| 
								 | 
							
								    <script type="module">
							 | 
						|||
| 
								 | 
							
								        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类--------------------------------------------------- */
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        /**
							 | 
						|||
| 
								 | 
							
								         * 创建三维热力图
							 | 
						|||
| 
								 | 
							
								         * @param {Cesium.Viewer} viewer 地图viewer对象
							 | 
						|||
| 
								 | 
							
								         * @param {Object} options 基础参数
							 | 
						|||
| 
								 | 
							
								         * @param {Array} options.dataPoints 热力值数组
							 | 
						|||
| 
								 | 
							
								         * @param {Array} options.radius 热力点半径
							 | 
						|||
| 
								 | 
							
								         * @param {Array} options.baseElevation 最低高度
							 | 
						|||
| 
								 | 
							
								         * @param {Array} options.colorGradient 颜色配置
							 | 
						|||
| 
								 | 
							
								         */
							 | 
						|||
| 
								 | 
							
								        function create3DHeatmap(viewer, options = {}) {
							 | 
						|||
| 
								 | 
							
								            const heatmapState = {
							 | 
						|||
| 
								 | 
							
								                viewer,
							 | 
						|||
| 
								 | 
							
								                options,
							 | 
						|||
| 
								 | 
							
								                dataPoints: options.dataPoints || [],
							 | 
						|||
| 
								 | 
							
								                containerElement: undefined,
							 | 
						|||
| 
								 | 
							
								                instanceId: Number(
							 | 
						|||
| 
								 | 
							
								                    `${new Date().getTime()}${Number(Math.random() * 1000).toFixed(0)}`
							 | 
						|||
| 
								 | 
							
								                ),
							 | 
						|||
| 
								 | 
							
								                canvasWidth: 200,
							 | 
						|||
| 
								 | 
							
								                boundingBox: undefined, // 四角坐标
							 | 
						|||
| 
								 | 
							
								                boundingRect: {}, // 经纬度范围
							 | 
						|||
| 
								 | 
							
								                xAxis: undefined, // x 轴
							 | 
						|||
| 
								 | 
							
								                yAxis: undefined, // y 轴
							 | 
						|||
| 
								 | 
							
								                xAxisLength: 0, // x轴长度
							 | 
						|||
| 
								 | 
							
								                yAxisLength: 0, // y轴长度
							 | 
						|||
| 
								 | 
							
								                baseElevation: options.baseElevation || 0,
							 | 
						|||
| 
								 | 
							
								                heatmapPrimitive: undefined,
							 | 
						|||
| 
								 | 
							
								                positionHierarchy: [],
							 | 
						|||
| 
								 | 
							
								                heatmapInstance: null,
							 | 
						|||
| 
								 | 
							
								            };
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								            if (!heatmapState.dataPoints || heatmapState.dataPoints.length < 2) {
							 | 
						|||
| 
								 | 
							
								                console.log("热力图点位不得少于3个!");
							 | 
						|||
| 
								 | 
							
								                return;
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								            createHeatmapContainer(heatmapState);
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								            const heatmapConfig = {
							 | 
						|||
| 
								 | 
							
								                container: document.getElementById(`heatmap-${heatmapState.instanceId}`),
							 | 
						|||
| 
								 | 
							
								                radius: options.radius || 20,
							 | 
						|||
| 
								 | 
							
								                maxOpacity: 0.7,
							 | 
						|||
| 
								 | 
							
								                minOpacity: 0,
							 | 
						|||
| 
								 | 
							
								                blur: 0.75,
							 | 
						|||
| 
								 | 
							
								                gradient: options.colorGradient || {
							 | 
						|||
| 
								 | 
							
								                    ".1": "blue",
							 | 
						|||
| 
								 | 
							
								                    ".5": "yellow",
							 | 
						|||
| 
								 | 
							
								                    ".7": "red",
							 | 
						|||
| 
								 | 
							
								                    ".99": "white",
							 | 
						|||
| 
								 | 
							
								                },
							 | 
						|||
| 
								 | 
							
								            };
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								            heatmapState.primitiveType = options.primitiveType || "TRIANGLES";
							 | 
						|||
| 
								 | 
							
								            heatmapState.heatmapInstance = h337.create(heatmapConfig);
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								            initializeHeatmap(heatmapState);
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								            return {
							 | 
						|||
| 
								 | 
							
								                destroy: () => destroyHeatmap(heatmapState),
							 | 
						|||
| 
								 | 
							
								                heatmapState,
							 | 
						|||
| 
								 | 
							
								            };
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        function initializeHeatmap(heatmapState) {
							 | 
						|||
| 
								 | 
							
								            for (const [index, dataPoint] of heatmapState.dataPoints.entries()) {
							 | 
						|||
| 
								 | 
							
								                const cartesianPosition = Cesium.Cartesian3.fromDegrees(
							 | 
						|||
| 
								 | 
							
								                    dataPoint.lnglat[0],
							 | 
						|||
| 
								 | 
							
								                    dataPoint.lnglat[1],
							 | 
						|||
| 
								 | 
							
								                    0
							 | 
						|||
| 
								 | 
							
								                );
							 | 
						|||
| 
								 | 
							
								                heatmapState.positionHierarchy.push(cartesianPosition);
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								            computeBoundingBox(heatmapState.positionHierarchy, heatmapState);
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								            const heatmapPoints = heatmapState.positionHierarchy.map(
							 | 
						|||
| 
								 | 
							
								                (position, index) => {
							 | 
						|||
| 
								 | 
							
								                    const normalizedCoords = computeNormalizedCoordinates(
							 | 
						|||
| 
								 | 
							
								                        position,
							 | 
						|||
| 
								 | 
							
								                        heatmapState
							 | 
						|||
| 
								 | 
							
								                    );
							 | 
						|||
| 
								 | 
							
								                    return {
							 | 
						|||
| 
								 | 
							
								                        x: normalizedCoords.x,
							 | 
						|||
| 
								 | 
							
								                        y: normalizedCoords.y,
							 | 
						|||
| 
								 | 
							
								                        value: heatmapState.dataPoints[index].value,
							 | 
						|||
| 
								 | 
							
								                    };
							 | 
						|||
| 
								 | 
							
								                }
							 | 
						|||
| 
								 | 
							
								            );
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								            heatmapState.heatmapInstance.addData(heatmapPoints);
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								            const geometryInstance = new Cesium.GeometryInstance({
							 | 
						|||
| 
								 | 
							
								                geometry: createHeatmapGeometry(heatmapState),
							 | 
						|||
| 
								 | 
							
								            });
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								            heatmapState.heatmapPrimitive = heatmapState.viewer.scene.primitives.add(
							 | 
						|||
| 
								 | 
							
								                new Cesium.Primitive({
							 | 
						|||
| 
								 | 
							
								                    geometryInstances: geometryInstance,
							 | 
						|||
| 
								 | 
							
								                    appearance: new Cesium.MaterialAppearance({
							 | 
						|||
| 
								 | 
							
								                        material: new Cesium.Material({
							 | 
						|||
| 
								 | 
							
								                            fabric: {
							 | 
						|||
| 
								 | 
							
								                                type: "Image",
							 | 
						|||
| 
								 | 
							
								                                uniforms: {
							 | 
						|||
| 
								 | 
							
								                                    image: heatmapState.heatmapInstance.getDataURL(),
							 | 
						|||
| 
								 | 
							
								                                },
							 | 
						|||
| 
								 | 
							
								                            },
							 | 
						|||
| 
								 | 
							
								                        }),
							 | 
						|||
| 
								 | 
							
								                        vertexShaderSource: `
							 | 
						|||
| 
								 | 
							
								        in vec3 position3DHigh;
							 | 
						|||
| 
								 | 
							
								        in vec3 position3DLow;
							 | 
						|||
| 
								 | 
							
								        in vec2 st;
							 | 
						|||
| 
								 | 
							
								        in float batchId;
							 | 
						|||
| 
								 | 
							
								        uniform sampler2D image_0; 
							 | 
						|||
| 
								 | 
							
								        out vec3 v_positionEC;
							 | 
						|||
| 
								 | 
							
								        in vec3 normal;
							 | 
						|||
| 
								 | 
							
								        out vec3 v_normalEC;
							 | 
						|||
| 
								 | 
							
								        out vec2 v_st; 
							 | 
						|||
| 
								 | 
							
								        void main(){
							 | 
						|||
| 
								 | 
							
								            vec4 p = czm_computePosition();
							 | 
						|||
| 
								 | 
							
								           
							 | 
						|||
| 
								 | 
							
								            v_normalEC = czm_normal * normal;   
							 | 
						|||
| 
								 | 
							
								            v_positionEC = (czm_modelViewRelativeToEye * p).xyz;
							 | 
						|||
| 
								 | 
							
								            vec4 positionWC=czm_inverseModelView* vec4(v_positionEC,1.0);
							 | 
						|||
| 
								 | 
							
								            v_st = st; 
							 | 
						|||
| 
								 | 
							
								            vec4 color = texture(image_0, v_st); 
							 | 
						|||
| 
								 | 
							
								            vec3 upDir = normalize(positionWC.xyz); 
							 | 
						|||
| 
								 | 
							
								            p += vec4(color.r *upDir * 1000., 0.0); 
							 | 
						|||
| 
								 | 
							
								            gl_Position = czm_modelViewProjectionRelativeToEye * p; 
							 | 
						|||
| 
								 | 
							
								        }`,
							 | 
						|||
| 
								 | 
							
								                        translucent: true,
							 | 
						|||
| 
								 | 
							
								                        flat: true,
							 | 
						|||
| 
								 | 
							
								                    }),
							 | 
						|||
| 
								 | 
							
								                    asynchronous: false,
							 | 
						|||
| 
								 | 
							
								                })
							 | 
						|||
| 
								 | 
							
								            );
							 | 
						|||
| 
								 | 
							
								            heatmapState.heatmapPrimitive.id = "heatmap3d";
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        function destroyHeatmap(heatmapState) {
							 | 
						|||
| 
								 | 
							
								            const containerElement = document.getElementById(
							 | 
						|||
| 
								 | 
							
								                `heatmap-${heatmapState.instanceId}`
							 | 
						|||
| 
								 | 
							
								            );
							 | 
						|||
| 
								 | 
							
								            if (containerElement) containerElement.remove();
							 | 
						|||
| 
								 | 
							
								            if (heatmapState.heatmapPrimitive) {
							 | 
						|||
| 
								 | 
							
								                heatmapState.viewer.scene.primitives.remove(heatmapState.heatmapPrimitive);
							 | 
						|||
| 
								 | 
							
								                heatmapState.heatmapPrimitive = undefined;
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        function computeNormalizedCoordinates(position, heatmapState) {
							 | 
						|||
| 
								 | 
							
								            if (!position) return;
							 | 
						|||
| 
								 | 
							
								            const cartographic = Cesium.Cartographic.fromCartesian(position.clone());
							 | 
						|||
| 
								 | 
							
								            cartographic.height = 0;
							 | 
						|||
| 
								 | 
							
								            position = Cesium.Cartographic.toCartesian(cartographic.clone());
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								            const originVector = Cesium.Cartesian3.subtract(
							 | 
						|||
| 
								 | 
							
								                position.clone(),
							 | 
						|||
| 
								 | 
							
								                heatmapState.boundingBox.leftTop,
							 | 
						|||
| 
								 | 
							
								                new Cesium.Cartesian3()
							 | 
						|||
| 
								 | 
							
								            );
							 | 
						|||
| 
								 | 
							
								            const xOffset = Cesium.Cartesian3.dot(originVector, heatmapState.xAxis);
							 | 
						|||
| 
								 | 
							
								            const yOffset = Cesium.Cartesian3.dot(originVector, heatmapState.yAxis);
							 | 
						|||
| 
								 | 
							
								            return {
							 | 
						|||
| 
								 | 
							
								                x: Number(
							 | 
						|||
| 
								 | 
							
								                    (xOffset / heatmapState.xAxisLength) * heatmapState.canvasWidth
							 | 
						|||
| 
								 | 
							
								                ).toFixed(0),
							 | 
						|||
| 
								 | 
							
								                y: Number(
							 | 
						|||
| 
								 | 
							
								                    (yOffset / heatmapState.yAxisLength) * heatmapState.canvasWidth
							 | 
						|||
| 
								 | 
							
								                ).toFixed(0),
							 | 
						|||
| 
								 | 
							
								            };
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        function cartesiansToLnglats(cartesians, viewer) {
							 | 
						|||
| 
								 | 
							
								            if (!cartesians || cartesians.length < 1) return;
							 | 
						|||
| 
								 | 
							
								            viewer = viewer || window.viewer;
							 | 
						|||
| 
								 | 
							
								            if (!viewer) {
							 | 
						|||
| 
								 | 
							
								                console.log("请传入viewer对象");
							 | 
						|||
| 
								 | 
							
								                return;
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								            var coordinates = [];
							 | 
						|||
| 
								 | 
							
								            for (var i = 0; i < cartesians.length; i++) {
							 | 
						|||
| 
								 | 
							
								                coordinates.push(cartesianToLnglat(cartesians[i], viewer));
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								            return coordinates;
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        function cartesianToLnglat(cartesian, viewer) {
							 | 
						|||
| 
								 | 
							
								            if (!cartesian) return [];
							 | 
						|||
| 
								 | 
							
								            viewer = viewer || window.viewer;
							 | 
						|||
| 
								 | 
							
								            var cartographic = Cesium.Cartographic.fromCartesian(cartesian);
							 | 
						|||
| 
								 | 
							
								            var latitude = Cesium.Math.toDegrees(cartographic.latitude);
							 | 
						|||
| 
								 | 
							
								            var longitude = Cesium.Math.toDegrees(cartographic.longitude);
							 | 
						|||
| 
								 | 
							
								            var height = cartographic.height;
							 | 
						|||
| 
								 | 
							
								            return [longitude, latitude, height];
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        function computeBoundingBox(positions, heatmapState) {
							 | 
						|||
| 
								 | 
							
								            if (!positions) return;
							 | 
						|||
| 
								 | 
							
								            const boundingSphere = Cesium.BoundingSphere.fromPoints(
							 | 
						|||
| 
								 | 
							
								                positions,
							 | 
						|||
| 
								 | 
							
								                new Cesium.BoundingSphere()
							 | 
						|||
| 
								 | 
							
								            );
							 | 
						|||
| 
								 | 
							
								            const centerPoint = boundingSphere.center;
							 | 
						|||
| 
								 | 
							
								            const sphereRadius = boundingSphere.radius;
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								            const modelMatrix = Cesium.Transforms.eastNorthUpToFixedFrame(
							 | 
						|||
| 
								 | 
							
								                centerPoint.clone()
							 | 
						|||
| 
								 | 
							
								            );
							 | 
						|||
| 
								 | 
							
								            const modelMatrixInverse = Cesium.Matrix4.inverse(
							 | 
						|||
| 
								 | 
							
								                modelMatrix.clone(),
							 | 
						|||
| 
								 | 
							
								                new Cesium.Matrix4()
							 | 
						|||
| 
								 | 
							
								            );
							 | 
						|||
| 
								 | 
							
								            const yAxisVector = new Cesium.Cartesian3(0, 1, 0);
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								            const boundingVertices = [];
							 | 
						|||
| 
								 | 
							
								            for (let angle = 45; angle <= 360; angle += 90) {
							 | 
						|||
| 
								 | 
							
								                const rotationMatrix = Cesium.Matrix3.fromRotationZ(
							 | 
						|||
| 
								 | 
							
								                    Cesium.Math.toRadians(angle),
							 | 
						|||
| 
								 | 
							
								                    new Cesium.Matrix3()
							 | 
						|||
| 
								 | 
							
								                );
							 | 
						|||
| 
								 | 
							
								                let rotatedYAxis = Cesium.Matrix3.multiplyByVector(
							 | 
						|||
| 
								 | 
							
								                    rotationMatrix,
							 | 
						|||
| 
								 | 
							
								                    yAxisVector,
							 | 
						|||
| 
								 | 
							
								                    new Cesium.Cartesian3()
							 | 
						|||
| 
								 | 
							
								                );
							 | 
						|||
| 
								 | 
							
								                rotatedYAxis = Cesium.Cartesian3.normalize(
							 | 
						|||
| 
								 | 
							
								                    rotatedYAxis,
							 | 
						|||
| 
								 | 
							
								                    new Cesium.Cartesian3()
							 | 
						|||
| 
								 | 
							
								                );
							 | 
						|||
| 
								 | 
							
								                const scaledVector = Cesium.Cartesian3.multiplyByScalar(
							 | 
						|||
| 
								 | 
							
								                    rotatedYAxis,
							 | 
						|||
| 
								 | 
							
								                    sphereRadius,
							 | 
						|||
| 
								 | 
							
								                    new Cesium.Cartesian3()
							 | 
						|||
| 
								 | 
							
								                );
							 | 
						|||
| 
								 | 
							
								                const vertex = Cesium.Matrix4.multiplyByPoint(
							 | 
						|||
| 
								 | 
							
								                    modelMatrix,
							 | 
						|||
| 
								 | 
							
								                    scaledVector.clone(),
							 | 
						|||
| 
								 | 
							
								                    new Cesium.Cartesian3()
							 | 
						|||
| 
								 | 
							
								                );
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								                boundingVertices.push(vertex);
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								            const coordinates = cartesiansToLnglats(
							 | 
						|||
| 
								 | 
							
								                boundingVertices,
							 | 
						|||
| 
								 | 
							
								                heatmapState.viewer
							 | 
						|||
| 
								 | 
							
								            );
							 | 
						|||
| 
								 | 
							
								            let minLatitude = Number.MAX_VALUE,
							 | 
						|||
| 
								 | 
							
								                maxLatitude = Number.MIN_VALUE,
							 | 
						|||
| 
								 | 
							
								                minLongitude = Number.MAX_VALUE,
							 | 
						|||
| 
								 | 
							
								                maxLongitude = Number.MIN_VALUE;
							 | 
						|||
| 
								 | 
							
								            const vertexCount = boundingVertices.length;
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								            coordinates.forEach((coordinate) => {
							 | 
						|||
| 
								 | 
							
								                if (coordinate[0] < minLongitude) minLongitude = coordinate[0];
							 | 
						|||
| 
								 | 
							
								                if (coordinate[0] > maxLongitude) maxLongitude = coordinate[0];
							 | 
						|||
| 
								 | 
							
								                if (coordinate[1] < minLatitude) minLatitude = coordinate[1];
							 | 
						|||
| 
								 | 
							
								                if (coordinate[1] > maxLatitude) maxLatitude = coordinate[1];
							 | 
						|||
| 
								 | 
							
								            });
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								            const latitudeRange = maxLatitude - minLatitude;
							 | 
						|||
| 
								 | 
							
								            const longitudeRange = maxLongitude - minLongitude;
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								            heatmapState.boundingRect = {
							 | 
						|||
| 
								 | 
							
								                minLatitude: minLatitude - latitudeRange / vertexCount,
							 | 
						|||
| 
								 | 
							
								                maxLatitude: maxLatitude + latitudeRange / vertexCount,
							 | 
						|||
| 
								 | 
							
								                minLongitude: minLongitude - longitudeRange / vertexCount,
							 | 
						|||
| 
								 | 
							
								                maxLongitude: maxLongitude + longitudeRange / vertexCount,
							 | 
						|||
| 
								 | 
							
								            };
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								            heatmapState.boundingBox = {
							 | 
						|||
| 
								 | 
							
								                leftTop: Cesium.Cartesian3.fromDegrees(
							 | 
						|||
| 
								 | 
							
								                    heatmapState.boundingRect.minLongitude,
							 | 
						|||
| 
								 | 
							
								                    heatmapState.boundingRect.maxLatitude
							 | 
						|||
| 
								 | 
							
								                ),
							 | 
						|||
| 
								 | 
							
								                leftBottom: Cesium.Cartesian3.fromDegrees(
							 | 
						|||
| 
								 | 
							
								                    heatmapState.boundingRect.minLongitude,
							 | 
						|||
| 
								 | 
							
								                    heatmapState.boundingRect.minLatitude
							 | 
						|||
| 
								 | 
							
								                ),
							 | 
						|||
| 
								 | 
							
								                rightTop: Cesium.Cartesian3.fromDegrees(
							 | 
						|||
| 
								 | 
							
								                    heatmapState.boundingRect.maxLongitude,
							 | 
						|||
| 
								 | 
							
								                    heatmapState.boundingRect.maxLatitude
							 | 
						|||
| 
								 | 
							
								                ),
							 | 
						|||
| 
								 | 
							
								                rightBottom: Cesium.Cartesian3.fromDegrees(
							 | 
						|||
| 
								 | 
							
								                    heatmapState.boundingRect.maxLongitude,
							 | 
						|||
| 
								 | 
							
								                    heatmapState.boundingRect.minLatitude
							 | 
						|||
| 
								 | 
							
								                ),
							 | 
						|||
| 
								 | 
							
								            };
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								            heatmapState.xAxis = Cesium.Cartesian3.subtract(
							 | 
						|||
| 
								 | 
							
								                heatmapState.boundingBox.rightTop,
							 | 
						|||
| 
								 | 
							
								                heatmapState.boundingBox.leftTop,
							 | 
						|||
| 
								 | 
							
								                new Cesium.Cartesian3()
							 | 
						|||
| 
								 | 
							
								            );
							 | 
						|||
| 
								 | 
							
								            heatmapState.xAxis = Cesium.Cartesian3.normalize(
							 | 
						|||
| 
								 | 
							
								                heatmapState.xAxis,
							 | 
						|||
| 
								 | 
							
								                new Cesium.Cartesian3()
							 | 
						|||
| 
								 | 
							
								            );
							 | 
						|||
| 
								 | 
							
								            heatmapState.yAxis = Cesium.Cartesian3.subtract(
							 | 
						|||
| 
								 | 
							
								                heatmapState.boundingBox.leftBottom,
							 | 
						|||
| 
								 | 
							
								                heatmapState.boundingBox.leftTop,
							 | 
						|||
| 
								 | 
							
								                new Cesium.Cartesian3()
							 | 
						|||
| 
								 | 
							
								            );
							 | 
						|||
| 
								 | 
							
								            heatmapState.yAxis = Cesium.Cartesian3.normalize(
							 | 
						|||
| 
								 | 
							
								                heatmapState.yAxis,
							 | 
						|||
| 
								 | 
							
								                new Cesium.Cartesian3()
							 | 
						|||
| 
								 | 
							
								            );
							 | 
						|||
| 
								 | 
							
								            heatmapState.xAxisLength = Cesium.Cartesian3.distance(
							 | 
						|||
| 
								 | 
							
								                heatmapState.boundingBox.rightTop,
							 | 
						|||
| 
								 | 
							
								                heatmapState.boundingBox.leftTop
							 | 
						|||
| 
								 | 
							
								            );
							 | 
						|||
| 
								 | 
							
								            heatmapState.yAxisLength = Cesium.Cartesian3.distance(
							 | 
						|||
| 
								 | 
							
								                heatmapState.boundingBox.leftBottom,
							 | 
						|||
| 
								 | 
							
								                heatmapState.boundingBox.leftTop
							 | 
						|||
| 
								 | 
							
								            );
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        function createHeatmapGeometry(heatmapState) {
							 | 
						|||
| 
								 | 
							
								            const meshData = generateMeshData(heatmapState);
							 | 
						|||
| 
								 | 
							
								            const geometry = new Cesium.Geometry({
							 | 
						|||
| 
								 | 
							
								                attributes: new Cesium.GeometryAttributes({
							 | 
						|||
| 
								 | 
							
								                    position: new Cesium.GeometryAttribute({
							 | 
						|||
| 
								 | 
							
								                        componentDatatype: Cesium.ComponentDatatype.DOUBLE,
							 | 
						|||
| 
								 | 
							
								                        componentsPerAttribute: 3,
							 | 
						|||
| 
								 | 
							
								                        values: meshData.positions,
							 | 
						|||
| 
								 | 
							
								                    }),
							 | 
						|||
| 
								 | 
							
								                    st: new Cesium.GeometryAttribute({
							 | 
						|||
| 
								 | 
							
								                        componentDatatype: Cesium.ComponentDatatype.FLOAT,
							 | 
						|||
| 
								 | 
							
								                        componentsPerAttribute: 2,
							 | 
						|||
| 
								 | 
							
								                        values: new Float32Array(meshData.textureCoords),
							 | 
						|||
| 
								 | 
							
								                    }),
							 | 
						|||
| 
								 | 
							
								                }),
							 | 
						|||
| 
								 | 
							
								                indices: new Uint16Array(meshData.indices),
							 | 
						|||
| 
								 | 
							
								                primitiveType: Cesium.PrimitiveType[heatmapState.primitiveType],
							 | 
						|||
| 
								 | 
							
								                boundingSphere: Cesium.BoundingSphere.fromVertices(meshData.positions),
							 | 
						|||
| 
								 | 
							
								            });
							 | 
						|||
| 
								 | 
							
								            return geometry;
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        function generateMeshData(heatmapState) {
							 | 
						|||
| 
								 | 
							
								            const gridWidth = heatmapState.canvasWidth || 200;
							 | 
						|||
| 
								 | 
							
								            const gridHeight = heatmapState.canvasWidth || 200;
							 | 
						|||
| 
								 | 
							
								            const { maxLongitude, maxLatitude, minLongitude, minLatitude } =
							 | 
						|||
| 
								 | 
							
								                heatmapState.boundingRect;
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								            const longitudeStep = (maxLongitude - minLongitude) / gridWidth;
							 | 
						|||
| 
								 | 
							
								            const latitudeStep = (maxLatitude - minLatitude) / gridHeight;
							 | 
						|||
| 
								 | 
							
								            const positions = [];
							 | 
						|||
| 
								 | 
							
								            const textureCoords = [];
							 | 
						|||
| 
								 | 
							
								            const indices = [];
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								            for (let i = 0; i < gridWidth; i++) {
							 | 
						|||
| 
								 | 
							
								                const currentLongitude = minLongitude + longitudeStep * i;
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								                for (let j = 0; j < gridHeight; j++) {
							 | 
						|||
| 
								 | 
							
								                    const currentLatitude = minLatitude + latitudeStep * j;
							 | 
						|||
| 
								 | 
							
								                    const heatValue = heatmapState.heatmapInstance.getValueAt({
							 | 
						|||
| 
								 | 
							
								                        x: i,
							 | 
						|||
| 
								 | 
							
								                        y: j,
							 | 
						|||
| 
								 | 
							
								                    });
							 | 
						|||
| 
								 | 
							
								                    const cartesian3 = Cesium.Cartesian3.fromDegrees(
							 | 
						|||
| 
								 | 
							
								                        currentLongitude,
							 | 
						|||
| 
								 | 
							
								                        currentLatitude,
							 | 
						|||
| 
								 | 
							
								                        heatmapState.baseElevation + heatValue
							 | 
						|||
| 
								 | 
							
								                    );
							 | 
						|||
| 
								 | 
							
								                    positions.push(cartesian3.x, cartesian3.y, cartesian3.z);
							 | 
						|||
| 
								 | 
							
								                    textureCoords.push(i / gridWidth, j / gridHeight);
							 | 
						|||
| 
								 | 
							
								                    if (j !== gridHeight - 1 && i !== gridWidth - 1) {
							 | 
						|||
| 
								 | 
							
								                        indices.push(
							 | 
						|||
| 
								 | 
							
								                            i * gridHeight + j,
							 | 
						|||
| 
								 | 
							
								                            i * gridHeight + j + 1,
							 | 
						|||
| 
								 | 
							
								                            (i + 1) * gridHeight + j
							 | 
						|||
| 
								 | 
							
								                        );
							 | 
						|||
| 
								 | 
							
								                        indices.push(
							 | 
						|||
| 
								 | 
							
								                            (i + 1) * gridHeight + j,
							 | 
						|||
| 
								 | 
							
								                            (i + 1) * gridHeight + j + 1,
							 | 
						|||
| 
								 | 
							
								                            i * gridHeight + j + 1
							 | 
						|||
| 
								 | 
							
								                        );
							 | 
						|||
| 
								 | 
							
								                    }
							 | 
						|||
| 
								 | 
							
								                }
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								            return {
							 | 
						|||
| 
								 | 
							
								                positions,
							 | 
						|||
| 
								 | 
							
								                textureCoords,
							 | 
						|||
| 
								 | 
							
								                indices,
							 | 
						|||
| 
								 | 
							
								            };
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        function createHeatmapContainer(heatmapState) {
							 | 
						|||
| 
								 | 
							
								            heatmapState.containerElement = window.document.createElement("div");
							 | 
						|||
| 
								 | 
							
								            heatmapState.containerElement.id = `heatmap-${heatmapState.instanceId}`;
							 | 
						|||
| 
								 | 
							
								            heatmapState.containerElement.className = `heatmap`;
							 | 
						|||
| 
								 | 
							
								            heatmapState.containerElement.style.width = `${heatmapState.canvasWidth}px`;
							 | 
						|||
| 
								 | 
							
								            heatmapState.containerElement.style.height = `${heatmapState.canvasWidth}px`;
							 | 
						|||
| 
								 | 
							
								            heatmapState.containerElement.style.position = "absolute";
							 | 
						|||
| 
								 | 
							
								            heatmapState.containerElement.style.display = "none";
							 | 
						|||
| 
								 | 
							
								            const mapContainer = window.document.getElementById(
							 | 
						|||
| 
								 | 
							
								                heatmapState.viewer.container.id
							 | 
						|||
| 
								 | 
							
								            );
							 | 
						|||
| 
								 | 
							
								            mapContainer.appendChild(heatmapState.containerElement);
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        const DOM = document.getElementById("box");
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        const viewer = new Cesium.Viewer(DOM, {
							 | 
						|||
| 
								 | 
							
								            animation: false, //是否创建动画小器件,左下角仪表
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								            baseLayerPicker: false, //是否显示图层选择器,右上角图层选择按钮
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								            baseLayer: Cesium.ImageryLayer.fromProviderAsync(
							 | 
						|||
| 
								 | 
							
								                Cesium.ArcGisMapServerImageryProvider.fromUrl(
							 | 
						|||
| 
								 | 
							
								                    "https://services.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 {
							 | 
						|||
| 
								 | 
							
								                lnglat: [
							 | 
						|||
| 
								 | 
							
								                    116.46 + Math.random() * 0.1 * (Math.random() > 0.5 ? 1 : -1),
							 | 
						|||
| 
								 | 
							
								                    39.92 + Math.random() * 0.1 * (Math.random() > 0.5 ? 1 : -1),
							 | 
						|||
| 
								 | 
							
								                ],
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								                value: 1000 * Math.random(),
							 | 
						|||
| 
								 | 
							
								            };
							 | 
						|||
| 
								 | 
							
								        });
							 | 
						|||
| 
								 | 
							
								        // 创建热力图
							 | 
						|||
| 
								 | 
							
								        create3DHeatmap(viewer, {
							 | 
						|||
| 
								 | 
							
								            dataPoints: points,
							 | 
						|||
| 
								 | 
							
								            radius: 15,
							 | 
						|||
| 
								 | 
							
								            baseElevation: 0,
							 | 
						|||
| 
								 | 
							
								            primitiveType: "TRIANGLES",
							 | 
						|||
| 
								 | 
							
								            colorGradient: {
							 | 
						|||
| 
								 | 
							
								                ".3": "blue",
							 | 
						|||
| 
								 | 
							
								                ".5": "green",
							 | 
						|||
| 
								 | 
							
								                ".7": "yellow",
							 | 
						|||
| 
								 | 
							
								                ".95": "red",
							 | 
						|||
| 
								 | 
							
								            },
							 | 
						|||
| 
								 | 
							
								        });
							 | 
						|||
| 
								 | 
							
								        viewer.camera.flyTo({
							 | 
						|||
| 
								 | 
							
								            destination: Cesium.Cartesian3.fromDegrees(116.46, 39.92, 100000),
							 | 
						|||
| 
								 | 
							
								            orientation: {},
							 | 
						|||
| 
								 | 
							
								            duration: 3,
							 | 
						|||
| 
								 | 
							
								        });
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								    </script>
							 | 
						|||
| 
								 | 
							
								</body>
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								</html>
							 |