| @@ -0,0 +1,315 @@ | |||
| /** | |||
| * @Author: Caven | |||
| * @Date: 2020-01-03 09:38:21 | |||
| */ | |||
| import { Util } from '@dc-modules/utils' | |||
| import State from '@dc-modules/state/State' | |||
| import { LayerEventType, OverlayEventType, LayerEvent } from '@dc-modules/event' | |||
| import LayerType from './LayerType' | |||
| const { Cesium } = DC.Namespace | |||
| class Layer { | |||
| constructor(id) { | |||
| this._id = Util.uuid() | |||
| this._bid = id || Util.uuid() | |||
| this._delegate = undefined | |||
| this._viewer = undefined | |||
| this._state = undefined | |||
| this._show = true | |||
| this._cache = {} | |||
| this._attr = {} | |||
| this._layerEvent = new LayerEvent() | |||
| this._layerEvent.on(LayerEventType.ADD, this._onAdd, this) | |||
| this._layerEvent.on(LayerEventType.REMOVE, this._onRemove, this) | |||
| this._state = undefined | |||
| this.type = undefined | |||
| } | |||
| get layerId() { | |||
| return this._id | |||
| } | |||
| get id() { | |||
| return this._bid | |||
| } | |||
| get delegate() { | |||
| return this._delegate | |||
| } | |||
| set show(show) { | |||
| this._show = show | |||
| this._delegate && (this._delegate.show = this._show) | |||
| } | |||
| get show() { | |||
| return this._show | |||
| } | |||
| get layerEvent() { | |||
| return this._layerEvent | |||
| } | |||
| set attr(attr) { | |||
| this._attr = attr | |||
| } | |||
| get attr() { | |||
| return this._attr | |||
| } | |||
| get state() { | |||
| return this._state | |||
| } | |||
| /** | |||
| * The hook for added | |||
| * @private | |||
| */ | |||
| _addedHook() {} | |||
| /** | |||
| * The hook for removed | |||
| * @private | |||
| */ | |||
| _removedHook() {} | |||
| /** | |||
| * The layer added callback function | |||
| * Subclasses need to be overridden | |||
| * @param viewer | |||
| * @private | |||
| */ | |||
| _onAdd(viewer) { | |||
| this._viewer = viewer | |||
| if (!this._delegate) { | |||
| return | |||
| } | |||
| 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 = State.ADDED | |||
| } | |||
| /** | |||
| * The layer added callback function | |||
| * Subclasses need to be overridden | |||
| * @private | |||
| */ | |||
| _onRemove() { | |||
| if (!this._delegate) { | |||
| return | |||
| } | |||
| if (this._viewer) { | |||
| this._cache = {} | |||
| if (this._delegate instanceof Cesium.PrimitiveCollection) { | |||
| this._delegate.removeAll() | |||
| this._viewer.scene.primitives.remove(this._delegate) | |||
| } else if (this._delegate.then) { | |||
| this._delegate.then(dataSource => { | |||
| dataSource.entities.removeAll() | |||
| }) | |||
| this._viewer.dataSources.remove(this._delegate) | |||
| } else { | |||
| this._delegate.entities && this._delegate.entities.removeAll() | |||
| this._viewer.dataSources.remove(this._delegate) | |||
| } | |||
| this._removedHook && this._removedHook() | |||
| this._state = State.REMOVED | |||
| } | |||
| } | |||
| /** | |||
| * The layer add overlay | |||
| * @param overlay | |||
| * @private | |||
| */ | |||
| _addOverlay(overlay) { | |||
| if ( | |||
| overlay && | |||
| overlay.overlayEvent && | |||
| !this._cache.hasOwnProperty(overlay.overlayId) | |||
| ) { | |||
| overlay.overlayEvent.fire(OverlayEventType.ADD, this) | |||
| this._cache[overlay.overlayId] = overlay | |||
| if (this._state === State.CLEARED) { | |||
| this._state = State.ADDED | |||
| } | |||
| } | |||
| } | |||
| /** | |||
| * The layer remove overlay | |||
| * @param overlay | |||
| * @private | |||
| */ | |||
| _removeOverlay(overlay) { | |||
| if ( | |||
| overlay && | |||
| overlay.overlayEvent && | |||
| this._cache.hasOwnProperty(overlay.overlayId) | |||
| ) { | |||
| overlay.overlayEvent.fire(OverlayEventType.REMOVE, this) | |||
| delete this._cache[overlay.overlayId] | |||
| } | |||
| } | |||
| /** | |||
| * Add overlay | |||
| * @param overlay | |||
| * @returns {Layer} | |||
| */ | |||
| addOverlay(overlay) { | |||
| this._addOverlay(overlay) | |||
| return this | |||
| } | |||
| /** | |||
| * Add overlays | |||
| * @param overlays | |||
| * @returns {Layer} | |||
| */ | |||
| addOverlays(overlays) { | |||
| if (Array.isArray(overlays)) { | |||
| overlays.forEach(item => { | |||
| this._addOverlay(item) | |||
| }) | |||
| } | |||
| return this | |||
| } | |||
| /** | |||
| * Remove overlay | |||
| * @param overlay | |||
| * @returns {Layer} | |||
| */ | |||
| removeOverlay(overlay) { | |||
| this._removeOverlay(overlay) | |||
| return this | |||
| } | |||
| /** | |||
| * Returns the overlay by overlayId | |||
| * @param overlayId | |||
| * @returns {*|undefined} | |||
| */ | |||
| getOverlay(overlayId) { | |||
| return this._cache[overlayId] || undefined | |||
| } | |||
| /** | |||
| * Returns the overlay by bid | |||
| * @param id | |||
| * @returns {any} | |||
| */ | |||
| getOverlayById(id) { | |||
| let overlay = undefined | |||
| Object.keys(this._cache).forEach(key => { | |||
| if (this._cache[key].id === id) { | |||
| overlay = this._cache[key] | |||
| } | |||
| }) | |||
| return overlay | |||
| } | |||
| /** | |||
| * Returns the overlays by attrName and AttrVal | |||
| * @param attrName | |||
| * @param attrVal | |||
| * @returns {[]} | |||
| */ | |||
| getOverlaysByAttr(attrName, attrVal) { | |||
| let result = [] | |||
| this.eachOverlay(item => { | |||
| if (item.attr[attrName] === attrVal) { | |||
| result.push(item) | |||
| } | |||
| }, this) | |||
| return result | |||
| } | |||
| /** | |||
| * Iterate through each overlay and pass it as an argument to the callback function | |||
| * @param method | |||
| * @param context | |||
| * @returns {Layer} | |||
| */ | |||
| eachOverlay(method, context) { | |||
| Object.keys(this._cache).forEach(key => { | |||
| method && method.call(context || this, this._cache[key]) | |||
| }) | |||
| return this | |||
| } | |||
| /** | |||
| * Returns all overlays | |||
| * @returns {[]} | |||
| */ | |||
| getOverlays() { | |||
| let result = [] | |||
| Object.keys(this._cache).forEach(key => { | |||
| result.push(this._cache[key]) | |||
| }) | |||
| return result | |||
| } | |||
| /** | |||
| * Clears all overlays | |||
| * Subclasses need to be overridden | |||
| */ | |||
| clear() {} | |||
| /** | |||
| * Removes from the viewer | |||
| */ | |||
| remove() { | |||
| if (this._viewer) { | |||
| this._viewer.removeLayer(this) | |||
| } | |||
| } | |||
| /** | |||
| * Adds to the viewer | |||
| * @param viewer | |||
| * @returns {Layer} | |||
| */ | |||
| addTo(viewer) { | |||
| if (viewer && viewer.addLayer) { | |||
| viewer.addLayer(this) | |||
| } | |||
| return this | |||
| } | |||
| /** | |||
| * sets the style, the style will apply to every overlay of the layer | |||
| * Subclasses need to be overridden | |||
| * @param style | |||
| */ | |||
| setStyle(style) {} | |||
| /** | |||
| * Registers Type | |||
| * @param type | |||
| */ | |||
| static registerType(type) { | |||
| if (type) { | |||
| LayerType[type.toLocaleUpperCase()] = type.toLocaleLowerCase() | |||
| } | |||
| } | |||
| /** | |||
| * Returns type | |||
| * @param type | |||
| * @returns {*|undefined} | |||
| */ | |||
| static getLayerType(type) { | |||
| return LayerType[type.toLocaleUpperCase()] || undefined | |||
| } | |||
| } | |||
| export default Layer | |||
| @@ -0,0 +1,8 @@ | |||
| /** | |||
| * @Author: Caven | |||
| * @Date: 2020-05-10 08:21:19 | |||
| */ | |||
| let LayerType = {} | |||
| export default LayerType | |||
| @@ -0,0 +1,23 @@ | |||
| /** | |||
| * @Author: Caven | |||
| * @Date: 2021-03-13 13:46:19 | |||
| */ | |||
| export { default as LayerType } from './LayerType' | |||
| export { default as Layer } from './Layer' | |||
| /** | |||
| * types | |||
| */ | |||
| export { default as ClusterLayer } from './type/ClusterLayer' | |||
| export { default as CzmlLayer } from './type/CzmlLayer' | |||
| export { default as GeoJsonLayer } from './type/GeoJsonLayer' | |||
| export { default as HeatLayer } from './type/HeatLayer' | |||
| export { default as HtmlLayer } from './type/HtmlLayer' | |||
| export { default as KmlLayer } from './type/KmlLayer' | |||
| export { default as LabelLayer } from './type/LabelLayer' | |||
| export { default as LayerGroup } from './type/LayerGroup' | |||
| export { default as PrimitiveLayer } from './type/PrimitiveLayer' | |||
| export { default as TilesetLayer } from './type/TilesetLayer' | |||
| export { default as TopoJsonLayer } from './type/TopoJsonLayer' | |||
| export { default as VectorLayer } from './type/VectorLayer' | |||
| @@ -0,0 +1,179 @@ | |||
| /** | |||
| * @Author: Caven | |||
| * @Date: 2020-02-10 10:05:41 | |||
| */ | |||
| import State from '@dc-modules/state/State' | |||
| import Layer from '../Layer' | |||
| const { Cesium } = DC.Namespace | |||
| const DEF_OPT = { | |||
| size: 18, | |||
| pixelRange: 40, | |||
| gradient: { | |||
| 0.0001: Cesium.Color.DEEPSKYBLUE, | |||
| 0.001: Cesium.Color.GREEN, | |||
| 0.01: Cesium.Color.ORANGE, | |||
| 0.1: Cesium.Color.RED | |||
| }, | |||
| fontSize: 12, | |||
| fontColor: Cesium.Color.BLACK, | |||
| style: 'circle' | |||
| } | |||
| class ClusterLayer extends Layer { | |||
| constructor(id, options = {}) { | |||
| super(id) | |||
| this._delegate = new Cesium.CustomDataSource(id) | |||
| this._options = { | |||
| ...DEF_OPT, | |||
| ...options | |||
| } | |||
| this._delegate.clustering.enabled = true | |||
| this._delegate.clustering.clusterEvent.addEventListener( | |||
| this._clusterEventHandler, | |||
| this | |||
| ) | |||
| this._delegate.clustering.pixelRange = this._options.pixelRange | |||
| this.type = Layer.getLayerType('cluster') | |||
| this._state = State.INITIALIZED | |||
| } | |||
| set enableCluster(enableCluster) { | |||
| this._delegate.clustering.enabled = enableCluster | |||
| return this | |||
| } | |||
| /** | |||
| * | |||
| * @param color | |||
| * @param numLength | |||
| * @returns {*} | |||
| * @private | |||
| */ | |||
| _drawCircle(color, numLength) { | |||
| let size = this._options.size * (numLength + 1) | |||
| let key = color.toCssColorString() + '-' + size | |||
| if (!this._cache[key]) { | |||
| let canvas = document.createElement('canvas') | |||
| canvas.width = size | |||
| canvas.height = size | |||
| let context2D = canvas.getContext('2d') | |||
| context2D.save() | |||
| context2D.scale(size / 24, size / 24) //Added to auto-generated code to scale up to desired size. | |||
| context2D.fillStyle = color.withAlpha(0.2).toCssColorString() //Modified from auto-generated code. | |||
| context2D.beginPath() | |||
| context2D.arc(12, 12, 9, 0, 2 * Math.PI) | |||
| context2D.closePath() | |||
| context2D.fill() | |||
| context2D.beginPath() | |||
| context2D.arc(12, 12, 6, 0, 2 * Math.PI) | |||
| context2D.fillStyle = color.toCssColorString() | |||
| context2D.fill() | |||
| context2D.closePath() | |||
| context2D.restore() | |||
| this._cache[key] = canvas.toDataURL() | |||
| } | |||
| return this._cache[key] | |||
| } | |||
| /** | |||
| * | |||
| * @param color | |||
| * @param numLength | |||
| * @returns {*} | |||
| * @private | |||
| */ | |||
| _drawClustering(color, numLength) { | |||
| let size = this._options.size * (numLength + 1) | |||
| let key = color.toCssColorString() + '-' + size | |||
| let startAngle = -Math.PI / 12 | |||
| let angle = Math.PI / 2 | |||
| let intervalAngle = Math.PI / 6 | |||
| if (!this._cache[key]) { | |||
| let canvas = document.createElement('canvas') | |||
| canvas.width = size | |||
| canvas.height = size | |||
| let context2D = canvas.getContext('2d') | |||
| context2D.save() | |||
| context2D.scale(size / 24, size / 24) //Added to auto-generated code to scale up to desired size. | |||
| context2D.beginPath() | |||
| context2D.arc(12, 12, 6, 0, 2 * Math.PI) | |||
| context2D.fillStyle = color.toCssColorString() | |||
| context2D.fill() | |||
| context2D.closePath() | |||
| context2D.lineWidth = 2 | |||
| for (let i = 0; i < 3; i++) { | |||
| context2D.beginPath() | |||
| context2D.arc(12, 12, 8, startAngle, startAngle + angle, false) | |||
| context2D.strokeStyle = color.withAlpha(0.4).toCssColorString() | |||
| context2D.stroke() | |||
| context2D.arc(12, 12, 11, startAngle, startAngle + angle, false) | |||
| context2D.strokeStyle = color.withAlpha(0.2).toCssColorString() | |||
| context2D.stroke() | |||
| context2D.closePath() | |||
| startAngle = startAngle + angle + intervalAngle | |||
| } | |||
| context2D.restore() | |||
| this._cache[key] = canvas.toDataURL() | |||
| } | |||
| return this._cache[key] | |||
| } | |||
| /** | |||
| * | |||
| * @param {*} clusteredEntities | |||
| * @param {*} cluster | |||
| */ | |||
| _clusterEventHandler(clusteredEntities, cluster) { | |||
| if (!this._delegate.clustering.enabled) { | |||
| return | |||
| } | |||
| cluster.billboard.show = true | |||
| cluster.label.font = `bold ${this._options.fontSize}px sans-serif` | |||
| cluster.label.fillColor = this._options.fontColor | |||
| cluster.label.disableDepthTestDistance = Number.POSITIVE_INFINITY | |||
| if (this._delegate.entities.values.length) { | |||
| let allCount = this._delegate.entities.values.length || 0 | |||
| for (let key in this._options.gradient) { | |||
| if (clusteredEntities.length >= allCount * key) { | |||
| let numLength = String(clusteredEntities.length).length | |||
| if (this._options.style === 'circle') { | |||
| cluster.billboard.image = this._drawCircle( | |||
| this._options.gradient[key], | |||
| numLength | |||
| ) | |||
| } else { | |||
| cluster.billboard.image = this._drawClustering( | |||
| this._options.gradient[key], | |||
| numLength | |||
| ) | |||
| } | |||
| cluster.label.show = true | |||
| if (numLength === 1) { | |||
| cluster.label.pixelOffset = new Cesium.Cartesian2(-2, 3) | |||
| } else { | |||
| cluster.label.pixelOffset = new Cesium.Cartesian2( | |||
| -5 * (numLength - 1), | |||
| 5 | |||
| ) | |||
| } | |||
| } else if (clusteredEntities.length <= 1) { | |||
| cluster.label.show = false | |||
| } | |||
| } | |||
| } | |||
| } | |||
| clear() { | |||
| this._delegate.entities.removeAll() | |||
| this._cache = {} | |||
| this._state = State.CLEARED | |||
| return this | |||
| } | |||
| } | |||
| Layer.registerType('cluster') | |||
| export default ClusterLayer | |||
| @@ -0,0 +1,52 @@ | |||
| /** | |||
| * @Author: Caven | |||
| * @Date: 2020-01-19 13:38:48 | |||
| */ | |||
| import State from '@dc-modules/state/State' | |||
| import Layer from '../Layer' | |||
| const { Cesium } = DC.Namespace | |||
| class CzmlLayer extends Layer { | |||
| constructor(id, url = '', options = {}) { | |||
| super(id) | |||
| this._delegate = Cesium.CzmlDataSource.load(url, options) | |||
| this.type = Layer.getLayerType('czml') | |||
| this._state = State.INITIALIZED | |||
| } | |||
| set show(show) { | |||
| this._show = show | |||
| this._delegate && | |||
| this._delegate.then(dataSource => { | |||
| dataSource.show = this._show | |||
| }) | |||
| } | |||
| get show() { | |||
| return this._show | |||
| } | |||
| /** | |||
| * | |||
| * @param method | |||
| * @param context | |||
| * @returns {CzmlLayer} | |||
| */ | |||
| eachOverlay(method, context) { | |||
| if (this._delegate) { | |||
| this._delegate.then(dataSource => { | |||
| let entities = dataSource.entities.values | |||
| entities.forEach(item => { | |||
| method.call(context, item) | |||
| }) | |||
| }) | |||
| return this | |||
| } | |||
| } | |||
| } | |||
| Layer.registerType('czml') | |||
| export default CzmlLayer | |||
| @@ -0,0 +1,131 @@ | |||
| /** | |||
| * @Author: Caven | |||
| * @Date: 2020-01-13 10:13:53 | |||
| */ | |||
| import State from '@dc-modules/state/State' | |||
| import { Billboard, Polyline, Polygon, Model } from '@dc-modules/overlay' | |||
| import Layer from '../Layer' | |||
| import VectorLayer from './VectorLayer' | |||
| const { Cesium } = DC.Namespace | |||
| class GeoJsonLayer extends Layer { | |||
| constructor(id, url, options = {}) { | |||
| if (!url) { | |||
| throw new Error('GeoJsonLayer:the url invalid') | |||
| } | |||
| super(id) | |||
| this._delegate = Cesium.GeoJsonDataSource.load(url, options) | |||
| this.type = Layer.getLayerType('geojson') | |||
| this._state = State.INITIALIZED | |||
| } | |||
| set show(show) { | |||
| this._show = show | |||
| this._delegate && | |||
| this._delegate.then(dataSource => { | |||
| dataSource.show = this._show | |||
| }) | |||
| } | |||
| get show() { | |||
| return this._show | |||
| } | |||
| _createBillboard(entity) { | |||
| if (entity.position && entity.billboard) { | |||
| return Billboard.fromEntity(entity) | |||
| } | |||
| } | |||
| /** | |||
| * Returns polyline Entity | |||
| * @param entity | |||
| * @returns {any} | |||
| * @private | |||
| */ | |||
| _createPolyline(entity) { | |||
| if (entity.polyline) { | |||
| return Polyline.fromEntity(entity) | |||
| } | |||
| } | |||
| /** | |||
| * Returns polygon Entity | |||
| * @param entity | |||
| * @returns {any} | |||
| * @private | |||
| */ | |||
| _createPolygon(entity) { | |||
| if (entity.polygon) { | |||
| return Polygon.fromEntity(entity) | |||
| } | |||
| } | |||
| /** | |||
| * Returns model Entity | |||
| * @param entity | |||
| * @param modelUrl | |||
| * @returns {Model} | |||
| * @private | |||
| */ | |||
| _createModel(entity, modelUrl) { | |||
| if (entity) { | |||
| return Model.fromEntity(entity, modelUrl) | |||
| } | |||
| } | |||
| /** | |||
| * | |||
| * @param method | |||
| * @param context | |||
| * @returns {GeoJsonLayer} | |||
| */ | |||
| eachOverlay(method, context) { | |||
| if (this._delegate) { | |||
| this._delegate.then(dataSource => { | |||
| let entities = dataSource.entities.values | |||
| entities.forEach(item => { | |||
| method.call(context, item) | |||
| }) | |||
| }) | |||
| return this | |||
| } | |||
| } | |||
| /** | |||
| * Converts to VectorLayer | |||
| * @returns {VectorLayer} | |||
| */ | |||
| toVectorLayer() { | |||
| let layer = new VectorLayer(this.id) | |||
| this.eachOverlay(item => { | |||
| if (item.billboard) { | |||
| layer.addOverlay(this._createBillboard(item)) | |||
| } else if (item.polyline) { | |||
| layer.addOverlay(this._createPolyline(item)) | |||
| } else if (item.polygon) { | |||
| layer.addOverlay(this._createPolygon(item)) | |||
| } | |||
| }, this) | |||
| return layer | |||
| } | |||
| /** | |||
| * Converts to VectorLayer | |||
| * @param modelUrl | |||
| * @returns {VectorLayer} | |||
| */ | |||
| toModelLayer(modelUrl) { | |||
| let layer = new VectorLayer(this.id) | |||
| this.eachOverlay(item => { | |||
| layer.addOverlay(this._createModel(item, modelUrl)) | |||
| }, this) | |||
| return layer | |||
| } | |||
| } | |||
| Layer.registerType('geojson') | |||
| export default GeoJsonLayer | |||
| @@ -0,0 +1,283 @@ | |||
| /** | |||
| * @Author: Caven | |||
| * @Date: 2020-02-27 00:35:35 | |||
| */ | |||
| import State from '@dc-modules/state/State' | |||
| import { SceneEventType } from '@dc-modules/event' | |||
| import { Transform } from '@dc-modules/transform' | |||
| import Position from '@dc-modules/position/Position' | |||
| import { DomUtil, Util } from '@dc-modules/utils' | |||
| import Layer from '../Layer' | |||
| const { Cesium } = DC.Namespace | |||
| const h337 = require('heatmap.js/build/heatmap.min') | |||
| 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' | |||
| } | |||
| } | |||
| class HeatLayer 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.type = Layer.getLayerType('heat') | |||
| this._state = State.INITIALIZED | |||
| } | |||
| get options() { | |||
| return this._options | |||
| } | |||
| /** | |||
| * The hook for added | |||
| */ | |||
| _addedHook() { | |||
| this._redraw() | |||
| this._viewer.on(SceneEventType.CAMERA_MOVE_END, this._reset, this) | |||
| } | |||
| /** | |||
| * The hook for removed | |||
| */ | |||
| _removedHook() { | |||
| this._viewer.off(SceneEventType.CAMERA_MOVE_END, this._reset, this) | |||
| } | |||
| /** | |||
| * | |||
| * @param {*} position | |||
| */ | |||
| _transformWGS84ToHeatmap(position) { | |||
| position = Transform.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 | |||
| } | |||
| /** | |||
| * | |||
| * @returns {{east: *, south: *, north: *, west: *}} | |||
| * @private | |||
| */ | |||
| _getMBounds() { | |||
| let mWestSouth = Transform.transformWGS84ToMercator( | |||
| new Position(this._bounds.west, this._bounds.south) | |||
| ) | |||
| let mEastNorth = Transform.transformWGS84ToMercator( | |||
| new Position(this._bounds.east, this._bounds.north) | |||
| ) | |||
| return { | |||
| west: mWestSouth.lng, | |||
| south: mWestSouth.lat, | |||
| east: mEastNorth.lng, | |||
| north: mEastNorth.lat | |||
| } | |||
| } | |||
| /** | |||
| * | |||
| * @private | |||
| */ | |||
| _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 = 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 = Transform.transformMercatorToWGS84({ | |||
| lng: this._mBounds.west, | |||
| lat: this._mBounds.south | |||
| }) | |||
| let eastNorth = Transform.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 && 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 | |||
| }) | |||
| this._delegate.entities.remove(this._entity) | |||
| this._initEntity() | |||
| let material = new Cesium.ImageMaterialProperty({ | |||
| image: this._heat._renderer.canvas, | |||
| transparent: true | |||
| }) | |||
| 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 = Cesium.Math.bounds(this._positions) | |||
| this._redraw() | |||
| return this | |||
| } | |||
| /** | |||
| * | |||
| * @param {*} position | |||
| */ | |||
| addPosition(position) { | |||
| this._positions.push(position) | |||
| this._bounds = Cesium.Math.bounds(this._positions) | |||
| this._redraw() | |||
| return this | |||
| } | |||
| /** | |||
| * | |||
| * @param {*} options | |||
| */ | |||
| setOptions(options) { | |||
| Util.merge(this._options, options) | |||
| if (this._heat) { | |||
| this._options.spacing = this._options.radius * 1.5 | |||
| this._heat.configure(this._options) | |||
| } | |||
| return this | |||
| } | |||
| } | |||
| Layer.registerType('heat') | |||
| export default HeatLayer | |||
| @@ -0,0 +1,89 @@ | |||
| /** | |||
| * @Author: Caven | |||
| * @Date: 2020-02-12 21:43:33 | |||
| */ | |||
| import { DomUtil } from '@dc-modules/utils' | |||
| import State from '@dc-modules/state/State' | |||
| import { Transform } from '@dc-modules/transform' | |||
| import Layer from '../Layer' | |||
| const { Cesium } = DC.Namespace | |||
| class HtmlLayer extends Layer { | |||
| constructor(id) { | |||
| super(id) | |||
| this._delegate = DomUtil.create('div', 'html-layer', undefined) | |||
| this._delegate.setAttribute('id', this._id) | |||
| this._renderRemoveCallback = undefined | |||
| this.type = Layer.getLayerType('html') | |||
| this._state = State.INITIALIZED | |||
| } | |||
| set show(show) { | |||
| this._show = show | |||
| this._delegate.style.visibility = this._show ? 'visible' : 'hidden' | |||
| this._cache.keys().forEach(key => { | |||
| this._cache[key].show = show | |||
| }) | |||
| return this | |||
| } | |||
| get show() { | |||
| return this._show | |||
| } | |||
| /** | |||
| * add handler | |||
| * @param viewer | |||
| * @private | |||
| */ | |||
| _onAdd(viewer) { | |||
| this._viewer = viewer | |||
| this._viewer.dcContainer.appendChild(this._delegate) | |||
| let scene = this._viewer.scene | |||
| this._renderRemoveCallback = scene.postRender.addEventListener(() => { | |||
| let cameraPosition = this._viewer.camera.positionWC | |||
| this.eachOverlay(item => { | |||
| if (item && item.position) { | |||
| let position = Transform.transformWGS84ToCartesian(item.position) | |||
| let windowCoord = Cesium.SceneTransforms.wgs84ToWindowCoordinates( | |||
| scene, | |||
| position | |||
| ) | |||
| let distance = Cesium.Cartesian3.distance(position, cameraPosition) | |||
| item._updateStyle({ transform: windowCoord }, distance) | |||
| } | |||
| }, this) | |||
| }, this) | |||
| this._state = State.ADDED | |||
| } | |||
| /** | |||
| * remove handler | |||
| * @returns {boolean} | |||
| * @private | |||
| */ | |||
| _onRemove() { | |||
| this._renderRemoveCallback && this._renderRemoveCallback() | |||
| this._viewer.dcContainer.removeChild(this._delegate) | |||
| this._state = State.REMOVED | |||
| } | |||
| /** | |||
| * Clears all divIcons | |||
| * @returns {HtmlLayer} | |||
| */ | |||
| clear() { | |||
| while (this._delegate.hasChildNodes()) { | |||
| this._delegate.removeChild(this._delegate.firstChild) | |||
| } | |||
| this._cache = {} | |||
| this._state = State.CLEARED | |||
| return this | |||
| } | |||
| } | |||
| Layer.registerType('html') | |||
| export default HtmlLayer | |||
| @@ -0,0 +1,49 @@ | |||
| /** | |||
| * @Author: Caven | |||
| * @Date: 2020-01-19 11:03:17 | |||
| */ | |||
| import State from '@dc-modules/state/State' | |||
| import Layer from '../Layer' | |||
| const { Cesium } = DC.Namespace | |||
| class KmlLayer extends Layer { | |||
| constructor(id, url, options = {}) { | |||
| if (!url) { | |||
| throw new Error('KmlLayer: the url is empty') | |||
| } | |||
| super(id) | |||
| this._delegate = Cesium.KmlDataSource.load(url, options) | |||
| this.type = Layer.getLayerType('kml') | |||
| this._state = State.INITIALIZED | |||
| } | |||
| set show(show) { | |||
| this._show = show | |||
| this._delegate && | |||
| this._delegate.then(dataSource => { | |||
| dataSource.show = this._show | |||
| }) | |||
| } | |||
| get show() { | |||
| return this._show | |||
| } | |||
| eachOverlay(method, context) { | |||
| if (this._delegate) { | |||
| this._delegate.then(dataSource => { | |||
| let entities = dataSource.entities.values | |||
| entities.forEach(item => { | |||
| method.call(context, item) | |||
| }) | |||
| }) | |||
| return this | |||
| } | |||
| } | |||
| } | |||
| Layer.registerType('kml') | |||
| export default KmlLayer | |||
| @@ -0,0 +1,41 @@ | |||
| /** | |||
| * @Author: Caven | |||
| * @Date: 2020-03-30 17:14:00 | |||
| */ | |||
| import State from '@dc-modules/state/State' | |||
| import { Label } from '@dc-modules/overlay' | |||
| import Layer from '../Layer' | |||
| const { Cesium } = DC.Namespace | |||
| class LabelLayer extends Layer { | |||
| constructor(id, url = '') { | |||
| super(id) | |||
| this._dataSource = Cesium.GeoJsonDataSource.load(url) | |||
| this._delegate = new Cesium.CustomDataSource(id) | |||
| this._initLabel() | |||
| this.type = Layer.registerType('label') | |||
| this._state = State.INITIALIZED | |||
| } | |||
| _createLabel(entity) { | |||
| if (entity.position && entity.name) { | |||
| return Label.fromEntity(entity) | |||
| } | |||
| } | |||
| _initLabel() { | |||
| this._dataSource.then(dataSource => { | |||
| let entities = dataSource.entities.values | |||
| entities.forEach(item => { | |||
| let label = this._createLabel(item) | |||
| this.addOverlay(label) | |||
| }) | |||
| }) | |||
| } | |||
| } | |||
| Layer.registerType('label') | |||
| export default LabelLayer | |||
| @@ -0,0 +1,143 @@ | |||
| /** | |||
| * @Author: Caven | |||
| * @Date: 2020-08-27 19:50:32 | |||
| */ | |||
| import { Util } from '@dc-modules/utils' | |||
| import State from '@dc-modules/state/State' | |||
| import { LayerGroupEventType, LayerGroupEvent } from '@dc-modules/event' | |||
| import Layer from '../Layer' | |||
| class LayerGroup { | |||
| constructor(id) { | |||
| this._id = id || Util.uuid() | |||
| this._cache = {} | |||
| this._show = true | |||
| this._viewer = undefined | |||
| this._layerGroupEvent = new LayerGroupEvent() | |||
| this._layerGroupEvent.on(LayerGroupEventType.ADD, this._onAdd, this) | |||
| this._layerGroupEvent.on(LayerGroupEventType.REMOVE, this._onRemove, this) | |||
| this.type = Layer.getLayerType('layer_group') | |||
| this._state = State.INITIALIZED | |||
| } | |||
| get id() { | |||
| return this._id | |||
| } | |||
| set show(show) { | |||
| this._show = show | |||
| Object.keys(this._cache).forEach(key => { | |||
| this._cache[key].show = this._show | |||
| }) | |||
| } | |||
| get show() { | |||
| return this._show | |||
| } | |||
| get layerGroupEvent() { | |||
| return this._layerGroupEvent | |||
| } | |||
| get state() { | |||
| return this._state | |||
| } | |||
| /** | |||
| * | |||
| * @param viewer | |||
| * @private | |||
| */ | |||
| _onAdd(viewer) { | |||
| this._viewer = viewer | |||
| Object.keys(this._cache).forEach(key => { | |||
| this._viewer.addLayer(this._cache[key]) | |||
| }) | |||
| this._state = State.ADDED | |||
| } | |||
| /** | |||
| * | |||
| * @private | |||
| */ | |||
| _onRemove() { | |||
| Object.keys(this._cache).forEach(key => { | |||
| this._viewer && this._viewer.removeLayer(this._cache[key]) | |||
| }) | |||
| this._cache = {} | |||
| this._state = State.REMOVED | |||
| } | |||
| /** | |||
| * Adds a layer | |||
| * @param layer | |||
| * @returns {LayerGroup} | |||
| */ | |||
| addLayer(layer) { | |||
| if (!Object(this._cache).hasOwnProperty(layer.id)) { | |||
| this._cache[layer.id] = layer | |||
| this._viewer && this._viewer.addLayer(layer) | |||
| } | |||
| return this | |||
| } | |||
| /** | |||
| * Removes a layer | |||
| * @param layer | |||
| * @returns {LayerGroup} | |||
| */ | |||
| removeLayer(layer) { | |||
| if (Object(this._cache).hasOwnProperty(layer.id)) { | |||
| this._viewer && this._viewer.removeLayer(layer) | |||
| delete this._cache[layer.id] | |||
| } | |||
| return this | |||
| } | |||
| /** | |||
| * Returns a layer by id | |||
| * @param id | |||
| * @returns {*|undefined} | |||
| */ | |||
| getLayer(id) { | |||
| return this._cache[id] || undefined | |||
| } | |||
| /** | |||
| * Returns all layers | |||
| * @returns {[]} | |||
| */ | |||
| getLayers() { | |||
| let result = [] | |||
| Object.keys(this._cache).forEach(key => { | |||
| result.push(this._cache[key]) | |||
| }) | |||
| return result | |||
| } | |||
| /** | |||
| * Adds to the viewer | |||
| * @param viewer | |||
| * @returns {LayerGroup} | |||
| */ | |||
| addTo(viewer) { | |||
| if (viewer && viewer.addLayerGroup) { | |||
| viewer.addLayerGroup(this) | |||
| } | |||
| return this | |||
| } | |||
| /** | |||
| * | |||
| * @returns {LayerGroup} | |||
| */ | |||
| remove() { | |||
| this._viewer && this._viewer.removeLayerGroup(this) | |||
| return this | |||
| } | |||
| } | |||
| Layer.registerType('layer_group') | |||
| export default LayerGroup | |||
| @@ -0,0 +1,33 @@ | |||
| /** | |||
| * @Author: Caven | |||
| * @Date: 2020-10-11 18:16:47 | |||
| */ | |||
| import State from '@dc-modules/state/State' | |||
| import Layer from '../Layer' | |||
| const { Cesium } = DC.Namespace | |||
| class PrimitiveLayer extends Layer { | |||
| constructor(id) { | |||
| super(id) | |||
| this._delegate = new Cesium.PrimitiveCollection() | |||
| this.type = Layer.getLayerType('primitive') | |||
| this._state = State.INITIALIZED | |||
| } | |||
| /** | |||
| * Clears all primitives | |||
| * @returns {PrimitiveLayer} | |||
| */ | |||
| clear() { | |||
| this._delegate && this._delegate.removeAll() | |||
| this._cache = {} | |||
| this._state = State.CLEARED | |||
| return this | |||
| } | |||
| } | |||
| Layer.registerType('primitive') | |||
| export default PrimitiveLayer | |||
| @@ -0,0 +1,36 @@ | |||
| /** | |||
| * @Author: Caven | |||
| * @Date: 2020-01-09 09:16:27 | |||
| */ | |||
| import State from '@dc-modules/state/State' | |||
| import Layer from '../Layer' | |||
| const { Cesium } = DC.Namespace | |||
| /** | |||
| * TilesetLayer is used to add various tileset | |||
| */ | |||
| class TilesetLayer extends Layer { | |||
| constructor(id) { | |||
| super(id) | |||
| this._delegate = new Cesium.PrimitiveCollection() | |||
| this.type = Layer.getLayerType('tileset') | |||
| this._state = State.INITIALIZED | |||
| } | |||
| /** | |||
| * Clear all tileset | |||
| * @returns {TilesetLayer} | |||
| */ | |||
| clear() { | |||
| this._delegate.removeAll() | |||
| this._cache = {} | |||
| this._state = State.CLEARED | |||
| return this | |||
| } | |||
| } | |||
| Layer.registerType('tileset') | |||
| export default TilesetLayer | |||
| @@ -0,0 +1,22 @@ | |||
| /** | |||
| * @Author: Caven | |||
| * @Date: 2020-09-11 19:32:22 | |||
| */ | |||
| import State from '@dc-modules/state/State' | |||
| import GeoJsonLayer from './GeoJsonLayer' | |||
| class TopoJsonLayer extends GeoJsonLayer { | |||
| constructor(id, url, options = {}) { | |||
| if (!url) { | |||
| throw new Error('TopoJsonLayer:the url invalid') | |||
| } | |||
| super(id, url, options) | |||
| this.type = GeoJsonLayer.getLayerType('topojson') | |||
| this._state = State.INITIALIZED | |||
| } | |||
| } | |||
| GeoJsonLayer.registerType('topojson') | |||
| export default TopoJsonLayer | |||
| @@ -0,0 +1,37 @@ | |||
| /** | |||
| * @Author: Caven | |||
| * @Date: 2020-01-02 16:42:03 | |||
| */ | |||
| import State from '@dc-modules/state/State' | |||
| import Layer from '../Layer' | |||
| const { Cesium } = DC.Namespace | |||
| /** | |||
| * The vector layer is used to add various entity, which is essentially a CustomDataSource | |||
| * that is used to place entities of the same class or business attribute into the same layer | |||
| */ | |||
| class VectorLayer extends Layer { | |||
| constructor(id) { | |||
| super(id) | |||
| this._delegate = new Cesium.CustomDataSource(id) | |||
| this.type = Layer.getLayerType('vector') | |||
| this._state = State.INITIALIZED | |||
| } | |||
| /** | |||
| * Clears all entities | |||
| * @returns {VectorLayer} | |||
| */ | |||
| clear() { | |||
| this._delegate.entities && this._delegate.entities.removeAll() | |||
| this._cache = {} | |||
| this._state = State.CLEARED | |||
| return this | |||
| } | |||
| } | |||
| Layer.registerType('vector') | |||
| export default VectorLayer | |||