| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400 | <!DOCTYPE html><html><head>  <meta charset="UTF-8" />  <title>2025竹节草</title>  <!-- 使用CDN引入Cesium资源 -->  <link rel="stylesheet" href="https://cesium.com/downloads/cesiumjs/releases/1.98/Build/Cesium/Widgets/widgets.css">  <script src="https://cesium.com/downloads/cesiumjs/releases/1.98/Build/Cesium/Cesium.js"></script>  <script src="https://unpkg.com/axios/dist/axios.min.js"></script>  <!-- 使用CDN引入libgif.js -->  <script src="https://cdn.jsdelivr.net/npm/libgif-js@0.1.2/libgif.min.js"></script></head><body style="margin: 0; overflow: hidden; background: #fff; width: 100%; height: 100%; position: absolute; top: 0">  <div id="map" style="margin: 0 auto; width: 100%; height: 100%"></div>  <ul    style="position: absolute;z-index: 100;bottom: 30px;right: 10px;    color: #fff;background: #000000cc; list-style: none;;">    <li>      <span style=" border-radius: 10px;   width: 10px;height: 10px; display: inline-block; background: GREEN;"></span>      <span>热带低压</span>    </li>    <li>      <span style="  border-radius: 10px; width: 10px;height: 10px; display: inline-block; background: BLUE;"></span>      <span>热带风暴</span>    </li>    <li>      <span        style="    border-radius: 10px; width: 10px;height: 10px; display: inline-block; background: YELLOW;"></span>      <span>强热带风暴</span>    </li>    <li>      <span        style="  border-radius: 10px;  width: 10px;height: 10px; display: inline-block; background: #FBC712;"></span>      <span>台风</span>    </li>    <li>      <span style="   border-radius: 10px;  width: 10px;height: 10px; display: inline-block; background: RED;"></span>      <span>超强台风</span>    </li>  </ul>  <script type="text/javascript">    // 请替换为你自己的Cesium令牌     Cesium.Ion.defaultAccessToken = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiI1ODIwOGQ2Ny1hMTFhLTQ4OGQtODJhZi0wNmMzZGNhNjU5OWMiLCJpZCI6NTkzMTMsImlhdCI6MTYyMzk4ODQ4NX0.40CU0i0LswshdxVXAXEJgfEDJN3EK_jPbo_S8lece9E';    const viewer = new Cesium.Viewer('map', {});    viewer.camera.setView({      destination: Cesium.Cartesian3.fromDegrees(120, 20, 4025692.0)    });    let fengquanLayers = [];    initJJ();    initPoints();    let myEntityCollection = new Cesium.CustomDataSource("initPointsCollection");    viewer.dataSources.add(myEntityCollection);    // 历史点和线    function initPoints() {      // 尝试加载数据,如果失败使用备用数据      axios.get('./src/assets/Data/202508.json')        .then((response) => {          let points = response.data.points;          processPoints(points);        })        .catch(error => {          console.error('加载JSON数据失败,使用测试数据', error);          // 使用测试数据          let testPoints = [            {              "lng": 120,              "lat": 20,              "strong": "台风",              "forecast": [                {                  "forecastpoints": [                    {"lng": 121, "lat": 19},                    {"lng": 122, "lat": 18}                  ]                }              ]            },            {              "lng": 121,              "lat": 19.5,              "strong": "强台风",              "forecast": []            }          ];          processPoints(testPoints);        });    }    function processPoints(points) {      let lineArr = [];      points.forEach(element => {        let color = Cesium.Color.RED        lineArr.push(element.lng)        lineArr.push(element.lat)        if (element.strong == "热带低压") {          color = Cesium.Color.GREEN        } else if (element.strong == "热带风暴") {          color = Cesium.Color.BLUE        } else if (element.strong == "强热带风暴") {          color = Cesium.Color.YELLOW        } else if (element.strong == "台风") {          color = Cesium.Color.fromCssColorString("#FBC712")        } else if (element.strong == "强台风") {          color = Cesium.Color.PLUM        } else if (element.strong == "超强台风") {          color = Cesium.Color.RED        }        var entity1 = new Cesium.Entity({          position: Cesium.Cartesian3.fromDegrees(element.lng, element.lat),          point: {            pixelSize: 5,            color: color          },        });        myEntityCollection.entities.add(entity1);      });      viewer.entities.add({        polyline: {          positions: Cesium.Cartesian3.fromDegreesArray(lineArr),          width: 3,          clampToGround: true,          material: Cesium.Color.RED,        }      });            if (points.length > 0) {        initForeast(points[points.length - 1])        adds(points)      }    }    // 预测    function initForeast(data) {      let forecast = data.forecast || [];      let colorArr = [        Cesium.Color.fromCssColorString("#2D12FB"),        Cesium.Color.fromCssColorString("#15E5E7"),        Cesium.Color.fromCssColorString("#15E74A"),        Cesium.Color.fromCssColorString("#E76F15"),        Cesium.Color.fromCssColorString("#15D9E7"),      ];      forecast.forEach((ele, ii) => {        let lineArr = [];        ele.forecastpoints.forEach((e) => {          lineArr.push(e.lng)          lineArr.push(e.lat)          var entity1 = new Cesium.Entity({            position: Cesium.Cartesian3.fromDegrees(e.lng, e.lat),            point: {              pixelSize: 7,              color: colorArr[ii]            },          });          myEntityCollection.entities.add(entity1);        })        viewer.entities.add({          polyline: {            positions: Cesium.Cartesian3.fromDegreesArray(lineArr),            width: 2,            clampToGround: true,            material: new Cesium.PolylineDashMaterialProperty({              color: colorArr[ii]            }),          }        });      })    }    // 警戒线    function initJJ() {      // 24 线      viewer.entities.add({        name: '24',        polyline: {          positions: Cesium.Cartesian3.fromDegreesArray([            [127, 34],            [127, 22],            [119, 18],            [119, 11],            [113, 4.5],            [105, 0]          ].flat()),          width: 2,          material: Cesium.Color.RED,          clampToGround: true,        }      });      // 48 线      viewer.entities.add({        name: '48',        polyline: {          positions: Cesium.Cartesian3.fromDegreesArray([            [132, 34],            [132, 22],            [119, 0],            [105, 0]          ].flat()),          width: 2,          material: Cesium.Color.YELLOW,          clampToGround: true,        }      });      viewer.entities.add({        position: Cesium.Cartesian3.fromDegrees(126.129019, 29.104287),        label: {          fillColor: Cesium.Color.RED,          text: '24小时警戒线',          font: '14pt monospace',        }      });      viewer.entities.add({        position: Cesium.Cartesian3.fromDegrees(132, 20),        label: {          fillColor: Cesium.Color.YELLOW,          text: '48小时警戒线',          font: '14pt monospace',        }      });    }    let iii = 0;    let currentPointObj;    let tbentity;    function adds(data) {      addTB()      setInterval(function () {        // 防止数组越界        if (iii >= data.length) {          iii = 0;        }                let kkk = iii * 2        currentPointObj = {          lon: data[iii].lng,          lat: data[iii].lat,          circle7: {            radius1: 350 - kkk,            radius2: 450 - kkk,            radius3: 400 - kkk,            radius4: 350 - kkk,          },          circle10: {            radius1: 250 - kkk,            radius2: 270 - kkk,            radius3: 250 - kkk,            radius4: 220 - kkk,          },          circle12: {            radius1: 170 - kkk,            radius2: 150 - kkk,            radius3: 150 - kkk,            radius4: 170 - kkk,          }        };        tbentity.position = Cesium.Cartesian3.fromDegrees(data[iii].lng, data[iii].lat)        iii = (iii + 1) % data.length;        removeTFLayer();        addTyphoonCircle();      }, 200);    }    function addTB() {      let div = document.createElement("div");      let img = document.createElement("img");      div.appendChild(img);      img.src = './tf.gif'; // 确保该GIF文件存在或替换为有效路径      img.onload = () => {        let rub = new SuperGif({          gif: img        })        rub.load(() => {          tbentity = viewer.entities.add({            position: Cesium.Cartesian3.fromDegrees(75.166493, 39.9060534),            billboard: {              image: new Cesium.CallbackProperty(() => {                return rub.get_canvas().toDataURL("image/png");              }, false),              scale: 0.1,            },          });        });      };            // 处理GIF加载失败的情况      img.onerror = () => {        console.error('GIF文件加载失败,使用默认标记');        // 创建一个简单的替代标记        tbentity = viewer.entities.add({          position: Cesium.Cartesian3.fromDegrees(75.166493, 39.9060534),          point: {            pixelSize: 15,            color: Cesium.Color.RED,            outlineColor: Cesium.Color.WHITE,            outlineWidth: 2          }        });      };    }    function removeTFLayer() {      let arr = fengquanLayers;      for (let i = 0; i < arr.length; i++) {        viewer.entities.remove(arr[i])      }      fengquanLayers = [];    }        function addTyphoonCircle() {      if (!currentPointObj) return;            const circles = ["circle7", "circle10", "circle12"];      circles.forEach(item => {        let en = viewer.entities.add({          id: `tf_polygon_${item}`,          name: `tf_polygon_${item}`,          polygon: {            hierarchy:              new Cesium.CallbackProperty(() => {                let points = [];                if (currentPointObj[item]) {                  points = getTyphoonPolygonPoints(currentPointObj, item)                } else {                  points = []                }                return new Cesium.PolygonHierarchy(Cesium.Cartesian3.fromDegreesArray(points));              }, false),            material: Cesium.Color.ORANGE.withAlpha(0.05),            extrudedHeight: 1000,            outline: true,            outlineColor: Cesium.Color.ORANGE,            outlineWidth: 2,          },          polyline: {            positions:              new Cesium.CallbackProperty(() => {                let points = [];                if (currentPointObj[item]) {                  points = getTyphoonPolygonPoints(currentPointObj, item)                } else {                  points = []                }                return new Cesium.Cartesian3.fromDegreesArray(points);              }, false),            material: Cesium.Color.ORANGE,            width: 2,            height: 1000,          }        })        fengquanLayers.push(en)      })    }        // 获取台风圈面的坐标    function getTyphoonPolygonPoints(pointObj, cNum) {      let points = []      let center = [pointObj.lon * 1, pointObj.lat * 1]      let radiusList = [        pointObj[cNum]['radius1'],        pointObj[cNum]['radius2'],        pointObj[cNum]['radius3'],        pointObj[cNum]['radius4'],      ]      let startAngleList = [0, 90, 180, 270]      let fx, fy      startAngleList.forEach((startAngle, index) => {        let radius = radiusList[index] / 100        let pointNum = 90        let endAngle = startAngle + 90        let sin, cos, x, y, angle        for (let i = 0; i <= pointNum; i++) {          angle = startAngle + ((endAngle - startAngle) * i) / pointNum          sin = Math.sin((angle * Math.PI) / 180)          cos = Math.cos((angle * Math.PI) / 180)          x = center[0] + radius * sin          y = center[1] + radius * cos          points.push(x, y);          if (startAngle == 0 && i == 0) {            fx = x;            fy = y;          }        }      })      points.push(fx, fy);      return points    }  </script></body></html>
 |