You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407
  1. /**
  2. * @Author: Caven
  3. * @Date: 2020-01-19 11:21:48
  4. */
  5. import { Cesium } from '@dc-modules/namespace'
  6. import { TrackEvent, TrackEventType } from '@dc-modules/event'
  7. import State from '@dc-modules/state/State'
  8. import Parse from '@dc-modules/parse/Parse'
  9. import { Util } from '@dc-modules/utils'
  10. import { Transform } from '@dc-modules/transform'
  11. import { heading, distance } from '@dc-modules/math'
  12. import TrackViewMode from './TrackViewMode'
  13. const DEF_OPTS = {
  14. clampToGround: false,
  15. clampToTileset: false,
  16. interpolationType: 'Linear',
  17. interpolationDegree: 2
  18. }
  19. const DEF_PATH_STYLE = {
  20. width: 2,
  21. material: Cesium.Color.ORANGE,
  22. clampToGround: true,
  23. depthFailMaterial: Cesium.Color.ORANGE.withAlpha(0.8)
  24. }
  25. class Track {
  26. constructor(positions, duration, callback, options) {
  27. this._id = Util.uuid()
  28. this._bid = undefined
  29. this._positions = Parse.parsePositions(positions)
  30. this._duration = duration || 20
  31. this._callback = callback
  32. this._options = {
  33. ...DEF_OPTS,
  34. ...options
  35. }
  36. this._controller = undefined
  37. this._sampledPosition = undefined
  38. this._viewed = false
  39. this._delegate = new Cesium.Entity()
  40. this._pathPositions = []
  41. this._path = new Cesium.Entity({
  42. show: false,
  43. polyline: {
  44. positions: new Cesium.CallbackProperty(() => {
  45. return this._pathPositions
  46. })
  47. }
  48. })
  49. this._positionIndex = 0
  50. this._timeLine = []
  51. this._startTime = undefined
  52. this._endTime = undefined
  53. this._trackEvent = new TrackEvent()
  54. this._trackEvent.on(TrackEventType.POST_RENDER, this._onPostRender, this)
  55. this._trackEvent.on(TrackEventType.ADD, this._onAdd, this)
  56. this._trackEvent.on(TrackEventType.REMOVE, this._onRemove, this)
  57. this._trackEvent.on(
  58. TrackEventType.RESET_TIME_LINE,
  59. this._resetTimeLine,
  60. this
  61. )
  62. this._state = State.INITIALIZED
  63. }
  64. get trackId() {
  65. return this._id
  66. }
  67. set id(id) {
  68. this._bid = id
  69. return this
  70. }
  71. get id() {
  72. return this._bid
  73. }
  74. set positions(postions) {
  75. this._positions = Parse.parsePositions(postions)
  76. this._resetTimeLine({})
  77. return this
  78. }
  79. get positions() {
  80. return this._positions
  81. }
  82. set duration(duration) {
  83. this._duration = duration
  84. this._resetTimeLine({})
  85. return this
  86. }
  87. get duration() {
  88. return this._duration
  89. }
  90. set startTime(startTime) {
  91. if (startTime instanceof Date) {
  92. this._startTime = Cesium.JulianDate.fromDate(startTime)
  93. } else {
  94. this._startTime = startTime
  95. }
  96. this._resetTimeLine({})
  97. return this
  98. }
  99. get startTime() {
  100. return this._startTime
  101. }
  102. set viewed(viewed) {
  103. this._viewed = viewed
  104. return this
  105. }
  106. get viewed() {
  107. return this._viewed
  108. }
  109. get trackEvent() {
  110. return this._trackEvent
  111. }
  112. get state() {
  113. return this._state
  114. }
  115. /**
  116. * add to entities
  117. * @param controller
  118. * @private
  119. */
  120. _onAdd(controller) {
  121. if (!controller) {
  122. return false
  123. }
  124. this._controller = controller
  125. this._controller.delegate.add(this._delegate)
  126. this._controller.delegate.add(this._path)
  127. !this._startTime && (this._startTime = Cesium.JulianDate.now())
  128. this._state = State.ADDED
  129. }
  130. /**
  131. * remove from entities
  132. * @private
  133. */
  134. _onRemove() {
  135. if (!this._controller) {
  136. return false
  137. }
  138. this._controller.delegate.remove(this._delegate)
  139. this._controller.delegate.remove(this._path)
  140. this._viewed = false
  141. this._startTime = undefined
  142. this._state = State.REMOVED
  143. }
  144. /**
  145. *
  146. * @param viewer
  147. * @param viewOption
  148. * @private
  149. */
  150. _onPostRender({ viewer, viewOption }) {
  151. if (!this._startTime || !this._endTime) {
  152. return false
  153. }
  154. let now = Cesium.JulianDate.now()
  155. if (Cesium.JulianDate.lessThan(now, this._endTime)) {
  156. let p = this._sampledPosition.getValue(now)
  157. this._pathPositions.push(p)
  158. if (this._options.clampToTileset) {
  159. this._delegate.position = viewer.scene.clampToHeight(p, [
  160. this._delegate
  161. ])
  162. } else {
  163. this._delegate.position = p
  164. }
  165. let time = this._timeLine[this._positionIndex]
  166. if (time) {
  167. let timeDiff = Cesium.JulianDate.secondsDifference(now, time)
  168. if (timeDiff >= 0 && timeDiff <= 1) {
  169. let position = this._positions[this._positionIndex] || undefined
  170. if (position) {
  171. let mat = Cesium.Matrix3.fromQuaternion(
  172. this._delegate.orientation.getValue(now)
  173. )
  174. let mat4 = Cesium.Matrix4.fromRotationTranslation(mat, p)
  175. let hpr = Cesium.Transforms.fixedFrameToHeadingPitchRoll(mat4)
  176. position.heading = Cesium.Math.toDegrees(hpr.heading)
  177. position.pitch = Cesium.Math.toDegrees(hpr.pitch)
  178. position.roll = Cesium.Math.toDegrees(hpr.roll)
  179. }
  180. this._callback &&
  181. this._callback(
  182. position,
  183. this._positionIndex + 1 === this._positions.length
  184. )
  185. this._positionIndex += 1
  186. }
  187. }
  188. }
  189. this._setCameraView(viewer, viewOption)
  190. }
  191. /**
  192. * Sets camera position
  193. * @param viewer
  194. * @param viewOption
  195. * @private
  196. */
  197. _setCameraView(viewer, viewOption) {
  198. if (!this._viewed) {
  199. return false
  200. }
  201. let now = Cesium.JulianDate.now()
  202. if (Cesium.JulianDate.greaterThan(now, this._endTime)) {
  203. viewer.camera.lookAtTransform(Cesium.Matrix4.IDENTITY)
  204. viewer.delegate.trackedEntity &&
  205. (viewer.delegate.trackedEntity = undefined)
  206. this._viewed = false
  207. } else {
  208. let p = this._sampledPosition.getValue(now)
  209. let next_p = this._sampledPosition.getValue(
  210. Cesium.JulianDate.addSeconds(now, 1 / 60, new Cesium.JulianDate())
  211. )
  212. if (p && next_p) {
  213. if (
  214. viewOption?.mode === TrackViewMode.TRACKED &&
  215. viewer.delegate?.trackedEntity?.id !== this._delegate?.id
  216. ) {
  217. viewer.delegate.trackedEntity = this._delegate
  218. } else if (viewOption?.mode === TrackViewMode.FP) {
  219. viewer.camera.lookAt(
  220. p,
  221. new Cesium.HeadingPitchRange(
  222. heading(p, next_p),
  223. Cesium.Math.toRadians(viewOption?.pitch || 0),
  224. viewOption?.range || 10
  225. )
  226. )
  227. } else if (viewOption?.mode === TrackViewMode.TP) {
  228. viewer.camera.lookAt(
  229. p,
  230. new Cesium.HeadingPitchRange(
  231. 0,
  232. Cesium.Math.toRadians(viewOption?.pitch || -90),
  233. viewOption?.range || 1000
  234. )
  235. )
  236. }
  237. }
  238. }
  239. }
  240. /**
  241. *
  242. * @param params
  243. * @returns {boolean}
  244. * @private
  245. */
  246. _resetTimeLine(params) {
  247. if (!this._startTime || !this._duration || !this._positions?.length) {
  248. return false
  249. }
  250. let interval = 0
  251. let v = distance(this._positions) / this._duration
  252. this._timeLine = this._positions.map((item, index, arr) => {
  253. if (index !== 0) {
  254. interval += distance([arr[index - 1], item]) / v
  255. }
  256. return Cesium.JulianDate.addSeconds(
  257. this._startTime,
  258. interval,
  259. new Cesium.JulianDate()
  260. )
  261. })
  262. if (params?.stopTime && params?.duration) {
  263. this._duration += params.duration
  264. this._timeLine = this._timeLine.map(item => {
  265. if (Cesium.JulianDate.greaterThan(item, params.stopTime)) {
  266. item = Cesium.JulianDate.addSeconds(
  267. item,
  268. params.duration,
  269. new Cesium.JulianDate()
  270. )
  271. }
  272. return item
  273. })
  274. } else {
  275. this._pathPositions = []
  276. }
  277. this._sampledPosition = new Cesium.SampledPositionProperty()
  278. this._sampledPosition.addSamples(
  279. this._timeLine,
  280. Transform.transformWGS84ArrayToCartesianArray(this._positions)
  281. )
  282. this._sampledPosition.forwardExtrapolationType =
  283. Cesium.ExtrapolationType.HOLD
  284. /// setInterpolationOptions
  285. if (this._options.interpolationType === 'Hermite') {
  286. this._sampledPosition.setInterpolationOptions({
  287. interpolationDegree: this._options.interpolationDegree || 2,
  288. interpolationAlgorithm: Cesium.HermitePolynomialApproximation
  289. })
  290. } else if (this._options.interpolationType === 'Linear') {
  291. this._sampledPosition.setInterpolationOptions({
  292. interpolationDegree: this._options.interpolationDegree || 1,
  293. interpolationAlgorithm: Cesium.LinearApproximation
  294. })
  295. } else if (this._options.interpolationType === 'Lagrange') {
  296. this._sampledPosition.setInterpolationOptions({
  297. interpolationDegree: this._options.interpolationDegree || 5,
  298. interpolationAlgorithm: Cesium.LagrangePolynomialApproximation
  299. })
  300. }
  301. this._delegate.orientation = new Cesium.VelocityOrientationProperty(
  302. this._sampledPosition
  303. )
  304. this._endTime = this._timeLine[this._timeLine.length - 1]
  305. }
  306. /**
  307. * Adds Position
  308. * @param position
  309. * @param duration
  310. * @returns {Track}
  311. */
  312. addPosition(position, duration) {
  313. this._positions.push(Parse.parsePosition(position))
  314. this._duration += duration
  315. this._resetTimeLine({})
  316. return this
  317. }
  318. /**
  319. * Sets model
  320. * @param modelPath
  321. * @param style
  322. * @returns {Track}
  323. */
  324. setModel(modelPath, style) {
  325. this._delegate.model = {
  326. ...style,
  327. uri: modelPath,
  328. heightReference: this._options.clampToGround
  329. ? Cesium.HeightReference.CLAMP_TO_GROUND
  330. : Cesium.HeightReference.NONE
  331. }
  332. return this
  333. }
  334. /**
  335. * Sets billboard
  336. * @param icon
  337. * @param style
  338. * @returns {Track}
  339. */
  340. setBillboard(icon, style) {
  341. this._delegate.billboard = {
  342. ...style,
  343. image: icon,
  344. heightReference: this._options.clampToGround
  345. ? Cesium.HeightReference.CLAMP_TO_GROUND
  346. : Cesium.HeightReference.NONE
  347. }
  348. return this
  349. }
  350. /**
  351. * Sets label
  352. * @param text
  353. * @param style
  354. * @returns {Track}
  355. */
  356. setLabel(text, style) {
  357. this._delegate.label = {
  358. ...style,
  359. text: text,
  360. heightReference: this._options.clampToGround
  361. ? Cesium.HeightReference.CLAMP_TO_GROUND
  362. : Cesium.HeightReference.NONE
  363. }
  364. return this
  365. }
  366. /**
  367. *
  368. * @param visible
  369. * @param style
  370. * @returns {Track}
  371. */
  372. setPath(visible, style = {}) {
  373. this._path.show = !!visible
  374. Util.merge(this._path.polyline, DEF_PATH_STYLE, style)
  375. return this
  376. }
  377. }
  378. export default Track