mirror of
https://github.com/jiawanlong/Cesium-Examples.git
synced 2025-07-05 15:47:34 +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>
|