Cesium.html 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400
  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <meta charset="UTF-8" />
  5. <title>2025竹节草</title>
  6. <!-- 使用CDN引入Cesium资源 -->
  7. <link rel="stylesheet" href="https://cesium.com/downloads/cesiumjs/releases/1.98/Build/Cesium/Widgets/widgets.css">
  8. <script src="https://cesium.com/downloads/cesiumjs/releases/1.98/Build/Cesium/Cesium.js"></script>
  9. <script src="https://unpkg.com/axios/dist/axios.min.js"></script>
  10. <!-- 使用CDN引入libgif.js -->
  11. <script src="https://cdn.jsdelivr.net/npm/libgif-js@0.1.2/libgif.min.js"></script>
  12. </head>
  13. <body style="margin: 0; overflow: hidden; background: #fff; width: 100%; height: 100%; position: absolute; top: 0">
  14. <div id="map" style="margin: 0 auto; width: 100%; height: 100%"></div>
  15. <ul
  16. style="position: absolute;z-index: 100;bottom: 30px;right: 10px; color: #fff;background: #000000cc; list-style: none;;">
  17. <li>
  18. <span style=" border-radius: 10px; width: 10px;height: 10px; display: inline-block; background: GREEN;"></span>
  19. <span>热带低压</span>
  20. </li>
  21. <li>
  22. <span style=" border-radius: 10px; width: 10px;height: 10px; display: inline-block; background: BLUE;"></span>
  23. <span>热带风暴</span>
  24. </li>
  25. <li>
  26. <span
  27. style=" border-radius: 10px; width: 10px;height: 10px; display: inline-block; background: YELLOW;"></span>
  28. <span>强热带风暴</span>
  29. </li>
  30. <li>
  31. <span
  32. style=" border-radius: 10px; width: 10px;height: 10px; display: inline-block; background: #FBC712;"></span>
  33. <span>台风</span>
  34. </li>
  35. <li>
  36. <span style=" border-radius: 10px; width: 10px;height: 10px; display: inline-block; background: RED;"></span>
  37. <span>超强台风</span>
  38. </li>
  39. </ul>
  40. <script type="text/javascript">
  41. // 请替换为你自己的Cesium令牌
  42. Cesium.Ion.defaultAccessToken = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiI1ODIwOGQ2Ny1hMTFhLTQ4OGQtODJhZi0wNmMzZGNhNjU5OWMiLCJpZCI6NTkzMTMsImlhdCI6MTYyMzk4ODQ4NX0.40CU0i0LswshdxVXAXEJgfEDJN3EK_jPbo_S8lece9E';
  43. const viewer = new Cesium.Viewer('map', {});
  44. viewer.camera.setView({
  45. destination: Cesium.Cartesian3.fromDegrees(120, 20, 4025692.0)
  46. });
  47. let fengquanLayers = [];
  48. initJJ();
  49. initPoints();
  50. let myEntityCollection = new Cesium.CustomDataSource("initPointsCollection");
  51. viewer.dataSources.add(myEntityCollection);
  52. // 历史点和线
  53. function initPoints() {
  54. // 尝试加载数据,如果失败使用备用数据
  55. axios.get('./src/assets/Data/202508.json')
  56. .then((response) => {
  57. let points = response.data.points;
  58. processPoints(points);
  59. })
  60. .catch(error => {
  61. console.error('加载JSON数据失败,使用测试数据', error);
  62. // 使用测试数据
  63. let testPoints = [
  64. {
  65. "lng": 120,
  66. "lat": 20,
  67. "strong": "台风",
  68. "forecast": [
  69. {
  70. "forecastpoints": [
  71. {"lng": 121, "lat": 19},
  72. {"lng": 122, "lat": 18}
  73. ]
  74. }
  75. ]
  76. },
  77. {
  78. "lng": 121,
  79. "lat": 19.5,
  80. "strong": "强台风",
  81. "forecast": []
  82. }
  83. ];
  84. processPoints(testPoints);
  85. });
  86. }
  87. function processPoints(points) {
  88. let lineArr = [];
  89. points.forEach(element => {
  90. let color = Cesium.Color.RED
  91. lineArr.push(element.lng)
  92. lineArr.push(element.lat)
  93. if (element.strong == "热带低压") {
  94. color = Cesium.Color.GREEN
  95. } else if (element.strong == "热带风暴") {
  96. color = Cesium.Color.BLUE
  97. } else if (element.strong == "强热带风暴") {
  98. color = Cesium.Color.YELLOW
  99. } else if (element.strong == "台风") {
  100. color = Cesium.Color.fromCssColorString("#FBC712")
  101. } else if (element.strong == "强台风") {
  102. color = Cesium.Color.PLUM
  103. } else if (element.strong == "超强台风") {
  104. color = Cesium.Color.RED
  105. }
  106. var entity1 = new Cesium.Entity({
  107. position: Cesium.Cartesian3.fromDegrees(element.lng, element.lat),
  108. point: {
  109. pixelSize: 5,
  110. color: color
  111. },
  112. });
  113. myEntityCollection.entities.add(entity1);
  114. });
  115. viewer.entities.add({
  116. polyline: {
  117. positions: Cesium.Cartesian3.fromDegreesArray(lineArr),
  118. width: 3,
  119. clampToGround: true,
  120. material: Cesium.Color.RED,
  121. }
  122. });
  123. if (points.length > 0) {
  124. initForeast(points[points.length - 1])
  125. adds(points)
  126. }
  127. }
  128. // 预测
  129. function initForeast(data) {
  130. let forecast = data.forecast || [];
  131. let colorArr = [
  132. Cesium.Color.fromCssColorString("#2D12FB"),
  133. Cesium.Color.fromCssColorString("#15E5E7"),
  134. Cesium.Color.fromCssColorString("#15E74A"),
  135. Cesium.Color.fromCssColorString("#E76F15"),
  136. Cesium.Color.fromCssColorString("#15D9E7"),
  137. ];
  138. forecast.forEach((ele, ii) => {
  139. let lineArr = [];
  140. ele.forecastpoints.forEach((e) => {
  141. lineArr.push(e.lng)
  142. lineArr.push(e.lat)
  143. var entity1 = new Cesium.Entity({
  144. position: Cesium.Cartesian3.fromDegrees(e.lng, e.lat),
  145. point: {
  146. pixelSize: 7,
  147. color: colorArr[ii]
  148. },
  149. });
  150. myEntityCollection.entities.add(entity1);
  151. })
  152. viewer.entities.add({
  153. polyline: {
  154. positions: Cesium.Cartesian3.fromDegreesArray(lineArr),
  155. width: 2,
  156. clampToGround: true,
  157. material: new Cesium.PolylineDashMaterialProperty({
  158. color: colorArr[ii]
  159. }),
  160. }
  161. });
  162. })
  163. }
  164. // 警戒线
  165. function initJJ() {
  166. // 24 线
  167. viewer.entities.add({
  168. name: '24',
  169. polyline: {
  170. positions: Cesium.Cartesian3.fromDegreesArray([
  171. [127, 34],
  172. [127, 22],
  173. [119, 18],
  174. [119, 11],
  175. [113, 4.5],
  176. [105, 0]
  177. ].flat()),
  178. width: 2,
  179. material: Cesium.Color.RED,
  180. clampToGround: true,
  181. }
  182. });
  183. // 48 线
  184. viewer.entities.add({
  185. name: '48',
  186. polyline: {
  187. positions: Cesium.Cartesian3.fromDegreesArray([
  188. [132, 34],
  189. [132, 22],
  190. [119, 0],
  191. [105, 0]
  192. ].flat()),
  193. width: 2,
  194. material: Cesium.Color.YELLOW,
  195. clampToGround: true,
  196. }
  197. });
  198. viewer.entities.add({
  199. position: Cesium.Cartesian3.fromDegrees(126.129019, 29.104287),
  200. label: {
  201. fillColor: Cesium.Color.RED,
  202. text: '24小时警戒线',
  203. font: '14pt monospace',
  204. }
  205. });
  206. viewer.entities.add({
  207. position: Cesium.Cartesian3.fromDegrees(132, 20),
  208. label: {
  209. fillColor: Cesium.Color.YELLOW,
  210. text: '48小时警戒线',
  211. font: '14pt monospace',
  212. }
  213. });
  214. }
  215. let iii = 0;
  216. let currentPointObj;
  217. let tbentity;
  218. function adds(data) {
  219. addTB()
  220. setInterval(function () {
  221. // 防止数组越界
  222. if (iii >= data.length) {
  223. iii = 0;
  224. }
  225. let kkk = iii * 2
  226. currentPointObj = {
  227. lon: data[iii].lng,
  228. lat: data[iii].lat,
  229. circle7: {
  230. radius1: 350 - kkk,
  231. radius2: 450 - kkk,
  232. radius3: 400 - kkk,
  233. radius4: 350 - kkk,
  234. },
  235. circle10: {
  236. radius1: 250 - kkk,
  237. radius2: 270 - kkk,
  238. radius3: 250 - kkk,
  239. radius4: 220 - kkk,
  240. },
  241. circle12: {
  242. radius1: 170 - kkk,
  243. radius2: 150 - kkk,
  244. radius3: 150 - kkk,
  245. radius4: 170 - kkk,
  246. }
  247. };
  248. tbentity.position = Cesium.Cartesian3.fromDegrees(data[iii].lng, data[iii].lat)
  249. iii = (iii + 1) % data.length;
  250. removeTFLayer();
  251. addTyphoonCircle();
  252. }, 200);
  253. }
  254. function addTB() {
  255. let div = document.createElement("div");
  256. let img = document.createElement("img");
  257. div.appendChild(img);
  258. img.src = './tf.gif'; // 确保该GIF文件存在或替换为有效路径
  259. img.onload = () => {
  260. let rub = new SuperGif({
  261. gif: img
  262. })
  263. rub.load(() => {
  264. tbentity = viewer.entities.add({
  265. position: Cesium.Cartesian3.fromDegrees(75.166493, 39.9060534),
  266. billboard: {
  267. image: new Cesium.CallbackProperty(() => {
  268. return rub.get_canvas().toDataURL("image/png");
  269. }, false),
  270. scale: 0.1,
  271. },
  272. });
  273. });
  274. };
  275. // 处理GIF加载失败的情况
  276. img.onerror = () => {
  277. console.error('GIF文件加载失败,使用默认标记');
  278. // 创建一个简单的替代标记
  279. tbentity = viewer.entities.add({
  280. position: Cesium.Cartesian3.fromDegrees(75.166493, 39.9060534),
  281. point: {
  282. pixelSize: 15,
  283. color: Cesium.Color.RED,
  284. outlineColor: Cesium.Color.WHITE,
  285. outlineWidth: 2
  286. }
  287. });
  288. };
  289. }
  290. function removeTFLayer() {
  291. let arr = fengquanLayers;
  292. for (let i = 0; i < arr.length; i++) {
  293. viewer.entities.remove(arr[i])
  294. }
  295. fengquanLayers = [];
  296. }
  297. function addTyphoonCircle() {
  298. if (!currentPointObj) return;
  299. const circles = ["circle7", "circle10", "circle12"];
  300. circles.forEach(item => {
  301. let en = viewer.entities.add({
  302. id: `tf_polygon_${item}`,
  303. name: `tf_polygon_${item}`,
  304. polygon: {
  305. hierarchy:
  306. new Cesium.CallbackProperty(() => {
  307. let points = [];
  308. if (currentPointObj[item]) {
  309. points = getTyphoonPolygonPoints(currentPointObj, item)
  310. } else {
  311. points = []
  312. }
  313. return new Cesium.PolygonHierarchy(Cesium.Cartesian3.fromDegreesArray(points));
  314. }, false),
  315. material: Cesium.Color.ORANGE.withAlpha(0.05),
  316. extrudedHeight: 1000,
  317. outline: true,
  318. outlineColor: Cesium.Color.ORANGE,
  319. outlineWidth: 2,
  320. },
  321. polyline: {
  322. positions:
  323. new Cesium.CallbackProperty(() => {
  324. let points = [];
  325. if (currentPointObj[item]) {
  326. points = getTyphoonPolygonPoints(currentPointObj, item)
  327. } else {
  328. points = []
  329. }
  330. return new Cesium.Cartesian3.fromDegreesArray(points);
  331. }, false),
  332. material: Cesium.Color.ORANGE,
  333. width: 2,
  334. height: 1000,
  335. }
  336. })
  337. fengquanLayers.push(en)
  338. })
  339. }
  340. // 获取台风圈面的坐标
  341. function getTyphoonPolygonPoints(pointObj, cNum) {
  342. let points = []
  343. let center = [pointObj.lon * 1, pointObj.lat * 1]
  344. let radiusList = [
  345. pointObj[cNum]['radius1'],
  346. pointObj[cNum]['radius2'],
  347. pointObj[cNum]['radius3'],
  348. pointObj[cNum]['radius4'],
  349. ]
  350. let startAngleList = [0, 90, 180, 270]
  351. let fx, fy
  352. startAngleList.forEach((startAngle, index) => {
  353. let radius = radiusList[index] / 100
  354. let pointNum = 90
  355. let endAngle = startAngle + 90
  356. let sin, cos, x, y, angle
  357. for (let i = 0; i <= pointNum; i++) {
  358. angle = startAngle + ((endAngle - startAngle) * i) / pointNum
  359. sin = Math.sin((angle * Math.PI) / 180)
  360. cos = Math.cos((angle * Math.PI) / 180)
  361. x = center[0] + radius * sin
  362. y = center[1] + radius * cos
  363. points.push(x, y);
  364. if (startAngle == 0 && i == 0) {
  365. fx = x;
  366. fy = y;
  367. }
  368. }
  369. })
  370. points.push(fx, fy);
  371. return points
  372. }
  373. </script>
  374. </body>
  375. </html>