| @@ -2,7 +2,7 @@ | |||
| * @Author: Caven | |||
| * @Date: 2020-01-03 09:38:21 | |||
| * @Last Modified by: Caven | |||
| * @Last Modified time: 2020-04-20 19:27:43 | |||
| * @Last Modified time: 2020-04-23 13:16:03 | |||
| */ | |||
| import Cesium from '@/namespace' | |||
| import { LayerEvent } from '@/core/event' | |||
| @@ -56,6 +56,16 @@ class Layer { | |||
| return this._state | |||
| } | |||
| /** | |||
| * The hook for added | |||
| */ | |||
| _addedHook() {} | |||
| /** | |||
| * The hook for removed | |||
| */ | |||
| _removedHook() {} | |||
| /** | |||
| * | |||
| * The layer added callback function | |||
| @@ -65,14 +75,12 @@ class Layer { | |||
| */ | |||
| _addHandler(viewer) { | |||
| this._viewer = viewer | |||
| if (!this._delegate) { | |||
| return false | |||
| } | |||
| if (this._delegate instanceof Cesium.PrimitiveCollection) { | |||
| this._viewer.scene.primitives.add(this._delegate) | |||
| } else { | |||
| this._viewer.dataSources.add(this._delegate) | |||
| } | |||
| this._addedHook && this._addedHook() | |||
| this._state = DC.LayerState.ADDED | |||
| } | |||
| @@ -98,6 +106,7 @@ class Layer { | |||
| this._delegate.entities && this._delegate.entities.removeAll() | |||
| this._viewer.dataSources.remove(this._delegate) | |||
| } | |||
| this._removedHook && this._removedHook() | |||
| this._state = DC.LayerState.REMOVED | |||
| } | |||
| } | |||
| @@ -0,0 +1,36 @@ | |||
| /* | |||
| * @Author: Caven | |||
| * @Date: 2020-04-23 09:29:56 | |||
| * @Last Modified by: Caven | |||
| * @Last Modified time: 2020-04-23 12:38:10 | |||
| */ | |||
| export default function getBounds(positions = [], expand = 0) { | |||
| let minLng = 180 | |||
| let minLat = 90 | |||
| let maxLng = -180 | |||
| let maxLat = -90 | |||
| positions | |||
| .filter(item => item instanceof DC.Position) | |||
| .forEach(item => { | |||
| minLng = Math.min(minLng, item.lng) | |||
| minLat = Math.min(minLat, item.lat) | |||
| maxLng = Math.max(maxLng, item.lng) | |||
| maxLat = Math.max(maxLat, item.lat) | |||
| }) | |||
| if (expand > 0) { | |||
| let diffLng = Math.abs(maxLng - maxLng) | |||
| let diffLat = Math.abs(maxLat - minLat) | |||
| minLng -= diffLng * expand | |||
| minLat -= diffLat * expand | |||
| maxLng += diffLng * expand | |||
| maxLat += diffLat * expand | |||
| } | |||
| return { | |||
| west: minLng, | |||
| south: minLat, | |||
| east: maxLng, | |||
| north: maxLat | |||
| } | |||
| } | |||
| @@ -2,12 +2,15 @@ | |||
| * @Author: Caven | |||
| * @Date: 2020-03-31 20:57:36 | |||
| * @Last Modified by: Caven | |||
| * @Last Modified time: 2020-04-16 20:27:49 | |||
| * @Last Modified time: 2020-04-23 09:40:08 | |||
| */ | |||
| import getBounds from './getBounds' | |||
| import getDistance from './getDistance' | |||
| import getHeading from './getHeading' | |||
| import isBetween from './isBetween' | |||
| DC.Math.getBounds = getBounds | |||
| DC.Math.getDistance = getDistance | |||
| DC.Math.getHeading = getHeading | |||
| DC.Math.isBetween = isBetween | |||
| @@ -2,7 +2,7 @@ | |||
| * @Author: Caven | |||
| * @Date: 2020-01-03 12:18:17 | |||
| * @Last Modified by: Caven | |||
| * @Last Modified time: 2020-04-20 10:31:54 | |||
| * @Last Modified time: 2020-04-22 22:43:45 | |||
| */ | |||
| import { OverlayEvent } from '@/core/event' | |||
| @@ -80,9 +80,6 @@ class Overlay { | |||
| * @param {*} layer | |||
| */ | |||
| _addHandler(layer) { | |||
| if (!layer) { | |||
| return false | |||
| } | |||
| this._layer = layer | |||
| this._mountedHook && this._mountedHook() | |||
| if (this._layer && this._layer.delegate && this._layer.delegate.entities) { | |||
| @@ -2,10 +2,12 @@ | |||
| * @Author: Caven | |||
| * @Date: 2020-01-07 09:00:32 | |||
| * @Last Modified by: Caven | |||
| * @Last Modified time: 2020-04-11 11:53:40 | |||
| * @Last Modified time: 2020-04-23 12:46:21 | |||
| */ | |||
| import Cesium from '@/namespace' | |||
| const WMP = new Cesium.WebMercatorProjection() | |||
| DC.T = class { | |||
| /** | |||
| * | |||
| @@ -82,4 +84,32 @@ DC.T = class { | |||
| ? WSG84Arr.map(item => DC.T.transformWSG84ToCartesian(item)) | |||
| : [] | |||
| } | |||
| /** | |||
| * | |||
| * @param {*} position | |||
| * | |||
| */ | |||
| static transformWgs84ToMercator(position) { | |||
| let mp = WMP.project( | |||
| Cesium.Cartographic.fromDegrees(position.lng, position.lat, position.alt) | |||
| ) | |||
| return new DC.Position(mp.x, mp.y, mp.z) | |||
| } | |||
| /** | |||
| * | |||
| * @param {*} position | |||
| * | |||
| */ | |||
| static transformMercatorToWgs84(position) { | |||
| let mp = WMP.unproject( | |||
| new Cesium.Cartesian3(position.lng, position.lat, position.alt) | |||
| ) | |||
| return new DC.Position( | |||
| Cesium.Math.toDegrees(mp.longitude), | |||
| Cesium.Math.toDegrees(mp.latitude), | |||
| mp.height | |||
| ) | |||
| } | |||
| } | |||
| @@ -0,0 +1,277 @@ | |||
| /* | |||
| * @Author: Caven | |||
| * @Date: 2020-02-27 00:35:35 | |||
| * @Last Modified by: Caven | |||
| * @Last Modified time: 2020-04-24 10:35:18 | |||
| */ | |||
| import Cesium from '@/namespace' | |||
| import Layer from '@/core/layer/Layer' | |||
| const h337 = require('heatmap') | |||
| const DEF_OPTS = { | |||
| 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 | |||
| maxCanvasSize: 2000, | |||
| minCanvasSize: 700, | |||
| radius: 25, | |||
| gradient: { | |||
| '0.4': 'blue', | |||
| '0.6': 'green', | |||
| '0.8': 'yellow', | |||
| '0.9': 'red' | |||
| } | |||
| } | |||
| DC.HeatLayer = class extends Layer { | |||
| constructor(id, options) { | |||
| super(id) | |||
| this._options = { | |||
| ...DEF_OPTS, | |||
| ...options | |||
| } | |||
| this._heat = undefined | |||
| this._bounds = undefined | |||
| this._mBounds = undefined | |||
| this._scale = 1 | |||
| this._positions = [] | |||
| this._options.spacing = this._options.radius * 1.5 | |||
| this._delegate = new Cesium.CustomDataSource(id) | |||
| this._entity = new Cesium.Entity() | |||
| this._state = DC.LayerState.INITIALIZED | |||
| this.type = DC.LayerType.HEAT | |||
| } | |||
| get options() { | |||
| return this._options | |||
| } | |||
| /** | |||
| * The hook for added | |||
| */ | |||
| _addedHook() { | |||
| this._reDraw() | |||
| this._viewer.on(DC.SceneEventType.CAMERA_MOVE_END, this._reset, this) | |||
| } | |||
| /** | |||
| * The hook for removed | |||
| */ | |||
| _removedHook() { | |||
| this._viewer.off(DC.SceneEventType.CAMERA_MOVE_END, this._reset, this) | |||
| } | |||
| /** | |||
| * | |||
| */ | |||
| _initContainer() {} | |||
| /** | |||
| * | |||
| * @param {*} position | |||
| */ | |||
| _transformWgs84ToHeatmap(position) { | |||
| position = DC.T.transformWgs84ToMercator(position) | |||
| let coord = {} | |||
| coord.x = Math.round( | |||
| (position.lng - this._mBounds.west) / this._scale + this._options.spacing | |||
| ) | |||
| coord.y = Math.round( | |||
| (position.lat - this._mBounds.south) / this._scale + this._options.spacing | |||
| ) | |||
| coord.y = this._heat._renderer.canvas.height - coord.y | |||
| return coord | |||
| } | |||
| /** | |||
| * | |||
| * @param {*} bounds | |||
| */ | |||
| _getMBounds() { | |||
| let mWestSouth = DC.T.transformWgs84ToMercator( | |||
| new DC.Position(this._bounds.west, this._bounds.south) | |||
| ) | |||
| let mEastNorth = DC.T.transformWgs84ToMercator( | |||
| new DC.Position(this._bounds.east, this._bounds.north) | |||
| ) | |||
| return { | |||
| west: mWestSouth.lng, | |||
| south: mWestSouth.lat, | |||
| east: mEastNorth.lng, | |||
| north: mEastNorth.lat | |||
| } | |||
| } | |||
| _initCanvas() { | |||
| let diffLng = Math.abs(this._mBounds.east - this._mBounds.west) | |||
| let diffLat = Math.abs(this._mBounds.north - this._mBounds.south) | |||
| let max = Math.max(diffLng, diffLat) | |||
| let min = Math.min(diffLng, diffLat) | |||
| let scale = 1 | |||
| if (max > this._options.maxCanvasSize) { | |||
| scale = max / this._options.maxCanvasSize | |||
| if (min / scale < this._options.minCanvasSize) { | |||
| scale = min / this._options.minCanvasSize | |||
| } | |||
| } else if (min < this._options.minCanvasSize) { | |||
| scale = min / this._options.minCanvasSize | |||
| if (max / scale > this._options.maxCanvasSize) { | |||
| scale = max / this._options.maxCanvasSize | |||
| } | |||
| } | |||
| this._scale = scale | |||
| if (!this._options.container) { | |||
| this._options.container = DC.DomUtil.create( | |||
| 'div', | |||
| 'heat-map', | |||
| document.getElementsByClassName('dc-container')[0] | |||
| ) | |||
| } | |||
| this._options.container.style.cssText = ` | |||
| width:${diffLng / this._scale}px; | |||
| height:${diffLat / this._scale}px; | |||
| margin:0; | |||
| display:none` | |||
| if (!this._heat) { | |||
| this._heat = h337.create(this._options) | |||
| } else { | |||
| this._heat.configure(this._options) | |||
| } | |||
| } | |||
| /** | |||
| * | |||
| */ | |||
| _initEntity() { | |||
| let offset = this._options.spacing * this._scale | |||
| this._mBounds.west -= offset | |||
| this._mBounds.south -= offset | |||
| this._mBounds.east += offset | |||
| this._mBounds.north += offset | |||
| let westSouth = DC.T.transformMercatorToWgs84({ | |||
| lng: this._mBounds.west, | |||
| lat: this._mBounds.south | |||
| }) | |||
| let eastNorth = DC.T.transformMercatorToWgs84({ | |||
| lng: this._mBounds.east, | |||
| lat: this._mBounds.north | |||
| }) | |||
| let bounds = Cesium.Rectangle.fromDegrees( | |||
| westSouth.lng, | |||
| westSouth.lat, | |||
| eastNorth.lng, | |||
| eastNorth.lat | |||
| ) | |||
| this._entity.show = false | |||
| this._entity.rectangle = { | |||
| coordinates: bounds, | |||
| fill: false, | |||
| distanceDisplayCondition: this._options.distanceDisplayCondition | |||
| } | |||
| this._delegate.entities.add(this._entity) | |||
| } | |||
| _reset() { | |||
| let cameraHeight = Math.floor( | |||
| this._viewer.camera.positionCartographic.height | |||
| ) | |||
| let radius = (1e4 / cameraHeight) * 60 | |||
| if (radius < 10) { | |||
| radius = 10 | |||
| } | |||
| if (radius > 60) { | |||
| radius = 60 | |||
| } | |||
| this._options.radius = radius | |||
| this._options.spacing = this._options.radius * 1.5 | |||
| this._heat.configure(this._options) | |||
| } | |||
| _reDraw() { | |||
| /** set bounds */ | |||
| if (!this._bounds) { | |||
| return false | |||
| } | |||
| let mBounds = this._getMBounds() | |||
| if ( | |||
| !this._mBounds || | |||
| mBounds.west !== this._mBounds.west || | |||
| mBounds.south !== this._mBounds.south || | |||
| mBounds.east !== this._mBounds.east || | |||
| mBounds.north !== this._mBounds.north | |||
| ) { | |||
| this._mBounds = mBounds | |||
| this._initCanvas() | |||
| } | |||
| let data = [] | |||
| this._positions.forEach(item => { | |||
| let coord = this._transformWgs84ToHeatmap({ | |||
| lng: item.lng || item.x, | |||
| lat: item.lat || item.y | |||
| }) | |||
| data.push({ | |||
| x: coord.x, | |||
| y: coord.y, | |||
| value: item.value || 1 | |||
| }) | |||
| }) | |||
| this._heat.setData({ | |||
| min: 0, | |||
| max: 1, | |||
| data | |||
| }) | |||
| if (!this._entity.rectangle) { | |||
| this._initEntity() | |||
| } | |||
| let material = new Cesium.ImageMaterialProperty({ | |||
| image: this._heat._renderer.canvas, | |||
| transparent: true | |||
| }) | |||
| DC.Util.merge(this._entity.rectangle, { | |||
| fill: true, | |||
| material: material | |||
| }) | |||
| this._entity.show = true | |||
| } | |||
| /** | |||
| * | |||
| * @param {*} positions | |||
| */ | |||
| setPositions(positions) { | |||
| if (!positions || !Array.isArray(positions)) { | |||
| return this | |||
| } | |||
| this._positions = positions | |||
| this._bounds = DC.Math.getBounds(this._positions) | |||
| this._reDraw() | |||
| return this | |||
| } | |||
| /** | |||
| * | |||
| * @param {*} position | |||
| */ | |||
| addPosition(position) { | |||
| this._positions.push(position) | |||
| this._bounds = DC.Math.getBounds(this._positions) | |||
| this._reDraw() | |||
| return this | |||
| } | |||
| /** | |||
| * | |||
| * @param {*} options | |||
| */ | |||
| setOptions(options) { | |||
| DC.Util.merge(this._options, options) | |||
| if (this._heat) { | |||
| this._options.spacing = this._options.radius * 1.5 | |||
| this._heat.configure(this._options) | |||
| } | |||
| return this | |||
| } | |||
| } | |||
| DC.LayerType.HEAT = 'heat' | |||
| @@ -1,246 +0,0 @@ | |||
| /* | |||
| * @Author: Caven | |||
| * @Date: 2020-02-27 00:35:35 | |||
| * @Last Modified by: Caven | |||
| * @Last Modified time: 2020-03-09 21:28:43 | |||
| */ | |||
| import Cesium from '@/namespace' | |||
| import Layer from '@/core/layer/Layer' | |||
| const h337 = require('heatmap') | |||
| const WMP = new Cesium.WebMercatorProjection() | |||
| const DEF_OPTS = { | |||
| 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 | |||
| xField: 'lng', | |||
| yField: 'lat', | |||
| gradient: { | |||
| '0.5': 'blue', | |||
| '0.8': 'red', | |||
| '0.95': 'white', | |||
| '0.6': 'yellow', | |||
| '0.5': 'green' | |||
| } | |||
| } | |||
| DC.HeatmapLayer = class extends Layer { | |||
| constructor(id, bounds, options) { | |||
| if (!bounds || bounds.length !== 2) { | |||
| throw new Error('the bounds is empty') | |||
| } | |||
| super(id) | |||
| this._options = { | |||
| ...DEF_OPTS, | |||
| ...options | |||
| } | |||
| this._delegate = new Cesium.CustomDataSource(id) | |||
| this._data = [] | |||
| this._width = 0 | |||
| this._height = 0 | |||
| this._scale = 1 | |||
| this._mBounds = this._computeMercatorBounds(bounds) | |||
| this._oriX = this._mBounds.west | |||
| this._oriY = this._mBounds.south | |||
| this._computeWidthAndHeight() | |||
| this._options.radius = Math.round( | |||
| this._options.radius | |||
| ? this._options.radius | |||
| : Math.max(this._width, this._height) / 60 | |||
| ) | |||
| this._spacing = this._options.radius * 1.5 | |||
| this._heatmapInstance = undefined | |||
| this._entity = new Cesium.Entity() | |||
| this._prepareHeatmap() | |||
| this._state = DC.LayerState.INITIALIZED | |||
| this.type = DC.LayerType.HEAT | |||
| } | |||
| /** | |||
| * | |||
| * @param {*} position | |||
| */ | |||
| _transformWgs84ToMercator(position) { | |||
| let mp = WMP.project( | |||
| Cesium.Cartographic.fromDegrees(position.lng, position.lat) | |||
| ) | |||
| return { | |||
| lng: mp.x, | |||
| lat: mp.y | |||
| } | |||
| } | |||
| /** | |||
| * | |||
| * @param {*} position | |||
| */ | |||
| _transformMercatorToWgs84(position) { | |||
| let mp = WMP.unproject(new Cesium.Cartesian3(position.lng, position.lat)) | |||
| return { | |||
| lng: Cesium.Math.toDegrees(mp.longitude), | |||
| lat: Cesium.Math.toDegrees(mp.latitude) | |||
| } | |||
| } | |||
| /** | |||
| * | |||
| * @param {*} position | |||
| */ | |||
| _transformWgs84ToHeatmap(position) { | |||
| position = this._transformWgs84ToMercator(position) | |||
| let coord = {} | |||
| coord.lng = Math.round( | |||
| (position.lng - this._oriX) / this._scale + this._spacing | |||
| ) | |||
| coord.lat = Math.round( | |||
| (position.lat - this._oriY) / this._scale + this._spacing | |||
| ) | |||
| coord.lat = this._height - coord.lat | |||
| return coord | |||
| } | |||
| /** | |||
| * | |||
| * @param {*} bounds | |||
| */ | |||
| _computeMercatorBounds(bounds) { | |||
| let mWestSouth = this._transformWgs84ToMercator(bounds[0]) | |||
| let mEastNorth = this._transformWgs84ToMercator(bounds[1]) | |||
| return { | |||
| west: mWestSouth.lng, | |||
| south: mWestSouth.lat, | |||
| east: mEastNorth.lng, | |||
| north: mEastNorth.lat | |||
| } | |||
| } | |||
| /** | |||
| * | |||
| */ | |||
| _computeWidthAndHeight() { | |||
| this._width = | |||
| this._mBounds.east > 0 && this._mBounds.west < 0 | |||
| ? this._mBounds.east + Math.abs(this._mBounds.west) | |||
| : Math.abs(this._mBounds.east - this._mBounds.west) | |||
| this._height = | |||
| this._mBounds.north > 0 && this._mBounds.south < 0 | |||
| ? this._mBounds.north + Math.abs(this._mBounds.south) | |||
| : Math.abs(this._mBounds.north - this._mBounds.south) | |||
| let maxCanvasSize = 2000 | |||
| let minCanvasSize = 700 | |||
| let max = Math.max(this._width, this._height) | |||
| let min = Math.min(this._width, this._height) | |||
| if (max > maxCanvasSize) { | |||
| this._scale = max / maxCanvasSize | |||
| if (min / this._scale < minCanvasSize) { | |||
| this._scale = min / minCanvasSize | |||
| } | |||
| } else if (min < minCanvasSize) { | |||
| this._scale = min / minCanvasSize | |||
| if (max > maxCanvasSize) { | |||
| this._scale = max / maxCanvasSize | |||
| } | |||
| } | |||
| this._width = this._width / this._scale | |||
| this._height = this._height / this._scale | |||
| } | |||
| /** | |||
| * | |||
| */ | |||
| _prepareHeatmap() { | |||
| let width = Math.round(this._width + this._spacing * 2) | |||
| let height = Math.round(this._height + this._spacing * 2) | |||
| let container = DC.DomUtil.create( | |||
| 'div', | |||
| 'heat-map', | |||
| document.getElementsByClassName('dc-container')[0] | |||
| ) | |||
| container.style.cssText = ` | |||
| width:${width}px; | |||
| height:${height}px; | |||
| margin:0; | |||
| display:none` | |||
| this._options.container = container | |||
| this._heatmapInstance = h337.create(this._options) | |||
| let offset = this._spacing * this._scale | |||
| let westSouth = this._transformMercatorToWgs84({ | |||
| lng: this._mBounds.west - offset, | |||
| lat: this._mBounds.south - offset | |||
| }) | |||
| let eastNorth = this._transformMercatorToWgs84({ | |||
| lng: this._mBounds.east + offset, | |||
| lat: this._mBounds.north + offset | |||
| }) | |||
| let bounds = Cesium.Rectangle.fromDegrees( | |||
| westSouth.lng, | |||
| westSouth.lat, | |||
| eastNorth.lng, | |||
| eastNorth.lat | |||
| ) | |||
| this._entity.show = false | |||
| this._entity.rectangle = { | |||
| coordinates: bounds, | |||
| fill: false, | |||
| distanceDisplayCondition: this._options.distanceDisplayCondition | |||
| } | |||
| this._delegate.entities.add(this._entity) | |||
| } | |||
| _parseData() { | |||
| let data = [] | |||
| if (!this._data || !this._data.length) { | |||
| return { | |||
| min: 0, | |||
| max: 0, | |||
| data | |||
| } | |||
| } | |||
| let min = this._data[0] && this._data[0].value ? this._data[0].value : 0 | |||
| let max = this._data[0] && this._data[0].value ? this._data[0].value : 1 | |||
| this._data.forEach(item => { | |||
| min = Math.min(min, item.value) | |||
| max = Math.max(max, item.value) | |||
| let coord = this._transformWgs84ToHeatmap({ | |||
| lng: item.lng || item.x, | |||
| lat: item.lat || item.y | |||
| }) | |||
| data.push({ | |||
| lng: coord.lng, | |||
| lat: coord.lat, | |||
| value: item.value || 0 | |||
| }) | |||
| }) | |||
| return { | |||
| min, | |||
| max, | |||
| data | |||
| } | |||
| } | |||
| _reDraw() { | |||
| this._heatmapInstance.setData(this._parseData()) | |||
| } | |||
| setPositions(positions) { | |||
| if (!positions || !Array.isArray(positions)) { | |||
| return this | |||
| } | |||
| this._data = positions | |||
| this._reDraw() | |||
| if (this._entity && this._entity.rectangle) { | |||
| let material = new Cesium.ImageMaterialProperty({ | |||
| image: this._heatmapInstance._renderer.canvas, | |||
| transparent: true | |||
| }) | |||
| this._entity.show = true | |||
| DC.Util.merge(this._entity.rectangle, { | |||
| fill: true, | |||
| material: material | |||
| }) | |||
| } | |||
| } | |||
| } | |||
| DC.LayerType.HEAT = 'heat' | |||
| @@ -2,9 +2,9 @@ | |||
| * @Author: Caven | |||
| * @Date: 2020-01-19 11:04:45 | |||
| * @Last Modified by: Caven | |||
| * @Last Modified time: 2020-04-09 20:36:29 | |||
| * @Last Modified time: 2020-04-23 15:16:53 | |||
| */ | |||
| import './DC.HeatmapLayer' | |||
| import './DC.HeatLayer' | |||
| import './DC.ClusterLayer' | |||
| import './DC.CzmlLayer' | |||
| import './DC.KmlLayer' | |||