Du kan inte välja fler än 25 ämnen Ämnen måste starta med en bokstav eller siffra, kan innehålla bindestreck ('-') och vara max 35 tecken långa.

Track.js 11KB

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