Ви не можете вибрати більше 25 тем Теми мають розпочинатися з літери або цифри, можуть містити дефіси (-) і не повинні перевищувати 35 символів.

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