/* * @Author: shuishen 1109946754@qq.com * @Date: 2023-05-06 20:24:41 * @LastEditors: shuishen 1109946754@qq.com * @LastEditTime: 2023-05-06 21:15:24 * @FilePath: \srs-police-affairs\src\utils\CesiumHeatmap.js * @Description: * * Copyright (c) 2023 by ${git_name_email}, All Rights Reserved. */ /* eslint-disable no-undef */ /* * CesiumHeatmap.js v0.1 | global.DC.Namespace.Cesium Heatmap Library * * Works with heatmap.js v2.0.0: http://www.patrick-wied.at/static/heatmapjs/ */ (function (window) { 'use strict' function define_CesiumHeatmap () { var CesiumHeatmap = { defaults: { useEntitiesIfAvailable: true, //whether to use entities if a Viewer is supplied or always use an ImageryProvider minCanvasSize: 700, // minimum size (in pixels) for the heatmap canvas maxCanvasSize: 2000, // maximum size (in pixels) for the heatmap canvas radiusFactor: 60, // data point size factor used if no radius is given (the greater of height and width divided by this number yields the used radius) spacingFactor: 1.5, // extra space around the borders (point radius multiplied by this number yields the spacing) maxOpacity: 0.8, // the maximum opacity used if not given in the heatmap options object minOpacity: 0.1, // the minimum opacity used if not given in the heatmap options object blur: 0.85, // the blur used if not given in the heatmap options object gradient: { // the gradient used if not given in the heatmap options object '0.9': 'red', '0.8': 'orange', '0.7': 'yellow', '0.5': 'blue', '0.3': 'green', }, } } /* Create a CesiumHeatmap instance * * cesium: the CesiumWidget or Viewer instance * bb: the WGS84 bounding box like {north, east, south, west} * options: a heatmap.js options object (see http://www.patrick-wied.at/static/heatmapjs/docs.html#h337-create) */ CesiumHeatmap.create = function (cesium, bb, options) { var instance = new CHInstance(cesium, bb, options) return instance } CesiumHeatmap._getContainer = function (width, height, id) { var c = document.createElement("div") if (id) { c.setAttribute("id", id) } c.setAttribute("style", "width: " + width + "px; height: " + height + "px; margin: 0px; display: none;") document.body.appendChild(c) return c } CesiumHeatmap._getImageryProvider = function (instance) { //var n = (new Date()).getTime(); var d = instance._heatmap.getDataURL() //console.log("Create data URL: " + ((new Date()).getTime() - n)); //var n = (new Date()).getTime(); var imgprov = new global.DC.Namespace.Cesium.SingleTileImageryProvider({ url: d, rectangle: instance._rectangle }) //console.log("Create imageryprovider: " + ((new Date()).getTime() - n)); imgprov._tilingScheme = new global.DC.Namespace.Cesium.WebMercatorTilingScheme({ rectangleSouthwestInMeters: new global.DC.Namespace.Cesium.Cartesian2(instance._mbounds.west, instance._mbounds.south), rectangleNortheastInMeters: new global.DC.Namespace.Cesium.Cartesian2(instance._mbounds.east, instance._mbounds.north) }) return imgprov } CesiumHeatmap._getID = function (len) { var text = "" var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" for (var i = 0; i < ((len) ? len : 8); i++) text += possible.charAt(Math.floor(Math.random() * possible.length)) return text } var WMP = new global.DC.Namespace.Cesium.WebMercatorProjection() var Cesium = global.DC.Namespace.Cesium /* Convert a WGS84 location into a mercator location * * p: the WGS84 location like {x: lon, y: lat} */ CesiumHeatmap.wgs84ToMercator = function (p) { var mp = WMP.project(Cesium.Cartographic.fromDegrees(p.x, p.y)) return { x: mp.x, y: mp.y } } /* Convert a WGS84 bounding box into a mercator bounding box * * bb: the WGS84 bounding box like {north, east, south, west} */ CesiumHeatmap.wgs84ToMercatorBB = function (bb) { var sw = WMP.project(Cesium.Cartographic.fromDegrees(bb.west, bb.south)) var ne = WMP.project(Cesium.Cartographic.fromDegrees(bb.east, bb.north)) return { north: ne.y, east: ne.x, south: sw.y, west: sw.x } } /* Convert a mercator location into a WGS84 location * * p: the mercator lcation like {x, y} */ CesiumHeatmap.mercatorToWgs84 = function (p) { var wp = WMP.unproject(new Cesium.Cartesian3(p.x, p.y)) return { x: wp.longitude, y: wp.latitude } } /* Convert a mercator bounding box into a WGS84 bounding box * * bb: the mercator bounding box like {north, east, south, west} */ CesiumHeatmap.mercatorToWgs84BB = function (bb) { var sw = WMP.unproject(new Cesium.Cartesian3(bb.west, bb.south)) var ne = WMP.unproject(new Cesium.Cartesian3(bb.east, bb.north)) return { north: this.rad2deg(ne.latitude), east: this.rad2deg(ne.longitude), south: this.rad2deg(sw.latitude), west: this.rad2deg(sw.longitude) } } /* Convert degrees into radians * * d: the degrees to be converted to radians */ CesiumHeatmap.deg2rad = function (d) { var r = d * (Math.PI / 180.0) return r } /* Convert radians into degrees * * r: the radians to be converted to degrees */ CesiumHeatmap.rad2deg = function (r) { var d = r / (Math.PI / 180.0) return d } return CesiumHeatmap } if (typeof (CesiumHeatmap) === 'undefined') { window.CesiumHeatmap = define_CesiumHeatmap(global.DC.Namespace.Cesium) } else { console.log("CesiumHeatmap already defined.") } })(window) /* Initiate a CesiumHeatmap instance * * c: CesiumWidget instance * bb: a WGS84 bounding box like {north, east, south, west} * o: a heatmap.js options object (see http://www.patrick-wied.at/static/heatmapjs/docs.html#h337-create) */ function CHInstance (c, bb, o) { if (!bb) { return null } if (!o) { o = {} } this._cesium = c this._options = o this._id = CesiumHeatmap._getID() this._options.gradient = ((this._options.gradient) ? this._options.gradient : CesiumHeatmap.defaults.gradient) this._options.maxOpacity = ((this._options.maxOpacity) ? this._options.maxOpacity : CesiumHeatmap.defaults.maxOpacity) this._options.minOpacity = ((this._options.minOpacity) ? this._options.minOpacity : CesiumHeatmap.defaults.minOpacity) this._options.blur = ((this._options.blur) ? this._options.blur : CesiumHeatmap.defaults.blur) this._mbounds = CesiumHeatmap.wgs84ToMercatorBB(bb) this._setWidthAndHeight(this._mbounds) this._options.radius = Math.round((this._options.radius) ? this._options.radius : ((this.width > this.height) ? this.width / CesiumHeatmap.defaults.radiusFactor : this.height / CesiumHeatmap.defaults.radiusFactor)) this._spacing = this._options.radius * CesiumHeatmap.defaults.spacingFactor this._xoffset = this._mbounds.west this._yoffset = this._mbounds.south this.width = Math.round(this.width + this._spacing * 2) this.height = Math.round(this.height + this._spacing * 2) this._mbounds.west -= this._spacing * this._factor this._mbounds.east += this._spacing * this._factor this._mbounds.south -= this._spacing * this._factor this._mbounds.north += this._spacing * this._factor this.bounds = CesiumHeatmap.mercatorToWgs84BB(this._mbounds) this._rectangle = global.DC.Namespace.Cesium.Rectangle.fromDegrees(this.bounds.west, this.bounds.south, this.bounds.east, this.bounds.north) this._container = CesiumHeatmap._getContainer(this.width, this.height, this._id) this._options.container = this._container this._heatmap = window.h337.create(this._options) this._container.children[0].setAttribute("id", this._id + "-hm") } /* Convert a WGS84 location to the corresponding heatmap location * * p: a WGS84 location like {x:lon, y:lat} */ CHInstance.prototype.wgs84PointToHeatmapPoint = function (p) { return this.mercatorPointToHeatmapPoint(CesiumHeatmap.wgs84ToMercator(p)) } /* Convert a mercator location to the corresponding heatmap location * * p: a WGS84 location like {x: lon, y:lat} */ CHInstance.prototype.mercatorPointToHeatmapPoint = function (p) { var pn = {} pn.x = Math.round((p.x - this._xoffset) / this._factor + this._spacing) pn.y = Math.round((p.y - this._yoffset) / this._factor + this._spacing) pn.y = this.height - pn.y return pn } CHInstance.prototype._setWidthAndHeight = function (mbb) { this.width = ((mbb.east > 0 && mbb.west < 0) ? mbb.east + Math.abs(mbb.west) : Math.abs(mbb.east - mbb.west)) this.height = ((mbb.north > 0 && mbb.south < 0) ? mbb.north + Math.abs(mbb.south) : Math.abs(mbb.north - mbb.south)) this._factor = 1 if (this.width > this.height && this.width > CesiumHeatmap.defaults.maxCanvasSize) { this._factor = this.width / CesiumHeatmap.defaults.maxCanvasSize if (this.height / this._factor < CesiumHeatmap.defaults.minCanvasSize) { this._factor = this.height / CesiumHeatmap.defaults.minCanvasSize } } else if (this.height > this.width && this.height > CesiumHeatmap.defaults.maxCanvasSize) { this._factor = this.height / CesiumHeatmap.defaults.maxCanvasSize if (this.width / this._factor < CesiumHeatmap.defaults.minCanvasSize) { this._factor = this.width / CesiumHeatmap.defaults.minCanvasSize } } else if (this.width < this.height && this.width < CesiumHeatmap.defaults.minCanvasSize) { this._factor = this.width / CesiumHeatmap.defaults.minCanvasSize if (this.height / this._factor > CesiumHeatmap.defaults.maxCanvasSize) { this._factor = this.height / CesiumHeatmap.defaults.maxCanvasSize } } else if (this.height < this.width && this.height < CesiumHeatmap.defaults.minCanvasSize) { this._factor = this.height / CesiumHeatmap.defaults.minCanvasSize if (this.width / this._factor > CesiumHeatmap.defaults.maxCanvasSize) { this._factor = this.width / CesiumHeatmap.defaults.maxCanvasSize } } this.width = this.width / this._factor this.height = this.height / this._factor } /* Set an array of heatmap locations * * min: the minimum allowed value for the data values * max: the maximum allowed value for the data values * data: an array of data points in heatmap coordinates and values like {x, y, value} */ CHInstance.prototype.setData = function (min, max, data) { if (data && data.length > 0 && min !== null && min !== false && max !== null && max !== false) { this._heatmap.setData({ min: min, max: max, data: data }) this.updateLayer() return true } return false } /* Set an array of WGS84 locations * * min: the minimum allowed value for the data values * max: the maximum allowed value for the data values * data: an array of data points in WGS84 coordinates and values like { x:lon, y:lat, value } */ CHInstance.prototype.setWGS84Data = function (min, max, data) { if (data && data.length > 0 && min !== null && min !== false && max !== null && max !== false) { var convdata = [] for (var i = 0; i < data.length; i++) { var gp = data[i] var hp = this.wgs84PointToHeatmapPoint(gp) if (gp.value || gp.value === 0) { hp.value = gp.value } convdata.push(hp) } return this.setData(min, max, convdata) } return false } /* Set whether or not the heatmap is shown on the map * * s: true means the heatmap is shown, false means the heatmap is hidden */ CHInstance.prototype.show = function (s) { if (this._layer) { this._layer.show = s } } /* Update/(re)draw the heatmap */ CHInstance.prototype.updateLayer = function () { // only works with a Viewer instance since the cesiumWidget // instance doesn't contain an entities property if (CesiumHeatmap.defaults.useEntitiesIfAvailable && this._cesium.entities) { if (this._layer) { this._cesium.entities.remove(this._layer) } // Work around issue with material rendering in global.DC.Namespace.Cesium // provided by https://github.com/criis material = new global.DC.Namespace.Cesium.ImageMaterialProperty({ image: this._heatmap._renderer.canvas, }) if (global.DC.Namespace.Cesium.VERSION >= "1.21") { material.transparent = true } else if (global.DC.Namespace.Cesium.VERSION >= "1.16") { material.alpha = 0.99 } this._layer = this._cesium.entities.add({ show: true, rectangle: { coordinates: this._rectangle, material: material } }) } else { if (this._layer) { this._cesium.scene.imageryLayers.remove(this._layer) } this._layer = this._cesium.scene.imageryLayers.addImageryProvider(CesiumHeatmap._getImageryProvider(this)) } }; /* DON'T TOUCH: * * heatmap.js v2.0.0 | JavaScript Heatmap Library: http://www.patrick-wied.at/static/heatmapjs/ * * Copyright 2008-2014 Patrick Wied - All rights reserved. * Dual licensed under MIT and Beerware license * * :: 2014-10-31 21:16 */ (function (a, b, c) { b[a] = c()//稍微改了下 })("h337", this, function () { var a = { defaultRadius: 40, defaultRenderer: "canvas2d", defaultGradient: { .25: "rgb(0,0,255)", .55: "rgb(0,255,0)", .85: "yellow", 1: "rgb(255,0,0)" }, defaultMaxOpacity: 1, defaultMinOpacity: 0, defaultBlur: .85, defaultXField: "x", defaultYField: "y", defaultValueField: "value", plugins: {} } var b = function h () { var b = function d (a) { this._coordinator = {} this._data = [] this._radi = [] this._min = 0 this._max = 1 this._xField = a["xField"] || a.defaultXField this._yField = a["yField"] || a.defaultYField this._valueField = a["valueField"] || a.defaultValueField if (a["radius"]) { this._cfgRadius = a["radius"] } } var c = a.defaultRadius b.prototype = { _organiseData: function (a, b) { var d = a[this._xField] var e = a[this._yField] var f = this._radi var g = this._data var h = this._max var i = this._min var j = a[this._valueField] || 1 var k = a.radius || this._cfgRadius || c if (!g[d]) { g[d] = [] f[d] = [] } if (!g[d][e]) { g[d][e] = j f[d][e] = k } else { g[d][e] += j } if (g[d][e] > h) { if (!b) { this._max = g[d][e] } else { this.setDataMax(g[d][e]) } return false } else { return { x: d, y: e, value: j, radius: k, min: i, max: h } } }, _unOrganizeData: function () { var a = [] var b = this._data var c = this._radi for (var d in b) { for (var e in b[d]) { a.push({ x: d, y: e, radius: c[d][e], value: b[d][e] }) } } return { min: this._min, max: this._max, data: a } }, _onExtremaChange: function () { this._coordinator.emit("extremachange", { min: this._min, max: this._max }) }, addData: function () { if (arguments[0].length > 0) { var a = arguments[0] var b = a.length while (b--) { this.addData.call(this, a[b]) } } else { var c = this._organiseData(arguments[0], true) if (c) { this._coordinator.emit("renderpartial", { min: this._min, max: this._max, data: [c] }) } } return this }, setData: function (a) { var b = a.data var c = b.length this._data = [] this._radi = [] for (var d = 0; d < c; d++) { this._organiseData(b[d], false) } this._max = a.max this._min = a.min || 0 this._onExtremaChange() this._coordinator.emit("renderall", this._getInternalData()) return this }, removeData: function () { }, setDataMax: function (a) { this._max = a this._onExtremaChange() this._coordinator.emit("renderall", this._getInternalData()) return this }, setDataMin: function (a) { this._min = a this._onExtremaChange() this._coordinator.emit("renderall", this._getInternalData()) return this }, setCoordinator: function (a) { this._coordinator = a }, _getInternalData: function () { return { max: this._max, min: this._min, data: this._data, radi: this._radi } }, getData: function () { return this._unOrganizeData() } } return b }() var c = function i () { var a = function (a) { var b = a.gradient || a.defaultGradient var c = document.createElement("canvas") var d = c.getContext("2d") c.width = 256 c.height = 1 var e = d.createLinearGradient(0, 0, 256, 1) for (var f in b) { e.addColorStop(f, b[f]) } d.fillStyle = e d.fillRect(0, 0, 256, 1) return d.getImageData(0, 0, 256, 1).data } var b = function (a, b) { var c = document.createElement("canvas") var d = c.getContext("2d") var e = a var f = a c.width = c.height = a * 2 if (b == 1) { d.beginPath() d.arc(e, f, a, 0, 2 * Math.PI, false) d.fillStyle = "rgba(0,0,0,1)" d.fill() } else { var g = d.createRadialGradient(e, f, a * b, e, f, a) g.addColorStop(0, "rgba(0,0,0,1)") g.addColorStop(1, "rgba(0,0,0,0)") d.fillStyle = g d.fillRect(0, 0, 2 * a, 2 * a) } return c } var c = function (a) { var b = [] var c = a.min var d = a.max var e = a.radi // eslint-disable-next-line no-redeclare var a = a.data var f = Object.keys(a) var g = f.length while (g--) { var h = f[g] var i = Object.keys(a[h]) var j = i.length while (j--) { var k = i[j] var l = a[h][k] var m = e[h][k] b.push({ x: h, y: k, value: l, radius: m }) } } return { min: c, max: d, data: b } } function d (b) { var c = b.container var d = this.shadowCanvas = document.createElement("canvas") var e = this.canvas = b.canvas || document.createElement("canvas") var f = this._renderBoundaries = [1e4, 1e4, 0, 0] var g = getComputedStyle(b.container) || {} e.className = "heatmap-canvas" this._width = e.width = d.width = +g.width.replace(/px/, "") this._height = e.height = d.height = +g.height.replace(/px/, "") this.shadowCtx = d.getContext("2d") this.ctx = e.getContext("2d") e.style.cssText = d.style.cssText = "position:absolute;left:0;top:0;" c.style.position = "relative" c.appendChild(e) this._palette = a(b) this._templates = {} this._setStyles(b) } d.prototype = { renderPartial: function (a) { this._drawAlpha(a) this._colorize() }, renderAll: function (a) { this._clear() this._drawAlpha(c(a)) this._colorize() }, _updateGradient: function (b) { this._palette = a(b) }, updateConfig: function (a) { if (a["gradient"]) { this._updateGradient(a) } this._setStyles(a) }, setDimensions: function (a, b) { this._width = a this._height = b this.canvas.width = this.shadowCanvas.width = a this.canvas.height = this.shadowCanvas.height = b }, _clear: function () { this.shadowCtx.clearRect(0, 0, this._width, this._height) this.ctx.clearRect(0, 0, this._width, this._height) }, _setStyles: function (a) { this._blur = a.blur == 0 ? 0 : a.blur || a.defaultBlur if (a.backgroundColor) { this.canvas.style.backgroundColor = a.backgroundColor } this._opacity = (a.opacity || 0) * 255 this._maxOpacity = (a.maxOpacity || a.defaultMaxOpacity) * 255 this._minOpacity = (a.minOpacity || a.defaultMinOpacity) * 255 this._useGradientOpacity = !!a.useGradientOpacity }, _drawAlpha: function (a) { var c = this._min = a.min var d = this._max = a.max // eslint-disable-next-line no-redeclare var a = a.data || [] var e = a.length var f = 1 - this._blur while (e--) { var g = a[e] var h = g.x var i = g.y var j = g.radius var k = Math.min(g.value, d) var l = h - j var m = i - j var n = this.shadowCtx var o if (!this._templates[j]) { this._templates[j] = o = b(j, f) } else { o = this._templates[j] } n.globalAlpha = (k - c) / (d - c) n.drawImage(o, l, m) if (l < this._renderBoundaries[0]) { this._renderBoundaries[0] = l } if (m < this._renderBoundaries[1]) { this._renderBoundaries[1] = m } if (l + 2 * j > this._renderBoundaries[2]) { this._renderBoundaries[2] = l + 2 * j } if (m + 2 * j > this._renderBoundaries[3]) { this._renderBoundaries[3] = m + 2 * j } } }, _colorize: function () { var a = this._renderBoundaries[0] var b = this._renderBoundaries[1] var c = this._renderBoundaries[2] - a var d = this._renderBoundaries[3] - b var e = this._width var f = this._height var g = this._opacity var h = this._maxOpacity var i = this._minOpacity var j = this._useGradientOpacity if (a < 0) { a = 0 } if (b < 0) { b = 0 } if (a + c > e) { c = e - a } if (b + d > f) { d = f - b } var k = this.shadowCtx.getImageData(a, b, c, d) var l = k.data var m = l.length var n = this._palette for (var o = 3; o < m; o += 4) { var p = l[o] var q = p * 4 if (!q) { continue } var r if (g > 0) { r = g } else { if (p < h) { if (p < i) { r = i } else { r = p } } else { r = h } } l[o - 3] = n[q] l[o - 2] = n[q + 1] l[o - 1] = n[q + 2] l[o] = j ? n[q + 3] : r } k.data = l this.ctx.putImageData(k, a, b) this._renderBoundaries = [1e3, 1e3, 0, 0] }, getValueAt: function (a) { var b var c = this.shadowCtx var d = c.getImageData(a.x, a.y, 1, 1) var e = d.data[3] var f = this._max var g = this._min b = Math.abs(f - g) * (e / 255) >> 0 return b }, getDataURL: function () { return this.canvas.toDataURL() } } return d }() var d = function j () { var b = false if (a["defaultRenderer"] === "canvas2d") { b = c } return b }() var e = { merge: function () { var a = {} var b = arguments.length for (var c = 0; c < b; c++) { var d = arguments[c] for (var e in d) { a[e] = d[e] } } return a } } var f = function k () { var c = function h () { function a () { this.cStore = {} } a.prototype = { on: function (a, b, c) { var d = this.cStore if (!d[a]) { d[a] = [] } d[a].push(function (a) { return b.call(c, a) }) }, emit: function (a, b) { var c = this.cStore if (c[a]) { var d = c[a].length for (var e = 0; e < d; e++) { var f = c[a][e] f(b) } } } } return a }() var f = function (a) { var b = a._renderer var c = a._coordinator var d = a._store c.on("renderpartial", b.renderPartial, b) c.on("renderall", b.renderAll, b) c.on("extremachange", function (b) { a._config.onExtremaChange && a._config.onExtremaChange({ min: b.min, max: b.max, gradient: a._config["gradient"] || a._config["defaultGradient"] }) }) d.setCoordinator(c) } function g () { var g = this._config = e.merge(a, arguments[0] || {}) this._coordinator = new c if (g["plugin"]) { var h = g["plugin"] if (!a.plugins[h]) { throw new Error("Plugin '" + h + "' not found. Maybe it was not registered.") } else { var i = a.plugins[h] this._renderer = new i.renderer(g) this._store = new i.store(g) } } else { this._renderer = new d(g) this._store = new b(g) } f(this) } g.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 (a) { this._config = e.merge(this._config, a) 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 (a) { if (this._store.getValueAt) { return this._store.getValueAt(a) } else if (this._renderer.getValueAt) { return this._renderer.getValueAt(a) } else { return null } } } return g }() var g = { create: function (a) { return new f(a) }, register: function (b, c) { a.plugins[b] = c } } return g })