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.

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