三維流體模組API開發-時間軸


[info] 小提示:

程式碼連結:https://doc-3dgdp.colife.org.tw/samplecode/#src/testweb/fluid_timeline/


初始化三維流體模組

三維流體模組需使用WebGL2,注意圖台初始化時要在參數中設定 requestWebGL2: true

index.html

  <!DOCTYPE html>
  <html>

  <head>
      <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
      <title></title>
      <meta charset="utf-8" />
      <!--引入js-->
      <script src="PGWeb3D.min.js"></script>      
      <!--引入css-->
      <link rel="stylesheet" type="text/css" href="PGWeb3D.css"/>
  </head>

  <body>
      <!-- 參數控制面板 -->
      <div id="MyControl" style="position: absolute;z-index:1;">
          <table style="background-color: black; color: white;">
              <tr>
                  <td>Shape</td>
                  <td id="shape">
                      <Input type="radio" name="shapeRadio" value="line" checked>Line</Input>
                      <Input type="radio" name="shapeRadio" value="arrow">Arrow</Input>
                  </td>
              </tr>
              <tr>
                  <td>MinSpeed</td>
                  <td><input type="number" id="minSpeed" style="width: 118px" step="0.1"></td>
              </tr>
              <tr>
                  <td>Speed Factor</td>
                  <td><input type="number" id="speedFactor" style="width: 118px"></td>
              </tr>
              <tr>
                  <td>Lift</td>
                  <td><input type="number" id="lift" style="width: 118px"></td>
              </tr>
              <tr>
                  <td>ArrowScale</td>
                  <td><input type="number" id="arrowScale" style="width: 118px"></td>
              </tr>
          </table>
      </div>

      <!-- 圖台初始化 -->    
      <div id="MyMap" style="width:100%;height:100%;position:absolute;top:0;left:0"></div>

      <!-- 引用主要 js,邏輯會寫在這個檔案 -->
      <script src="main.js"></script>

  </body>

  </html>

main.js

// 初始化圖台, 記得傳入 requestWebGL2: true 參數
var terrainview = new ov.TerrainView("MyMap", { requestWebGL2: true });

// 取得三維流體模組
var fluidModule = terrainview.getModule("fluid");

// 預先建立三維流體模組的參數物件
var fluidParam = {};

// 在使用其他圖層前必須要先讀入地形 其他圖層的新增都會在callback中
terrainview.openTerrain({
  url: "http://127.0.0.1:8080",
  identifier: "範例地形圖",
  callback: openCallback,
  urlTemplate: "https://sample.pilotgaea.com.tw/Oview.aspx?{URL}"
});

// 文字資料取得
const getText = async (url) => {
  const response = await fetch(url);
  return await response.text();
};

// 地形讀取完成後的callback
function openCallback(result) {
  //設定初始位置
  let initialPos = new GeoPoint(119.87511267160517, 23.069641713992965, 150);
  let initialV = new Geo3DPoint(0, 0, -1);
  let initialUp = new Geo3DPoint(0, 1, 0);
  let initialCamera = new ov.Camera(initialPos, initialV, initialUp);
  terrainview.gotoCamera(initialCamera, false);

  // 設定地形檔的WMTS影像
  // ov.TerrainView.setBaseLayer(param)
  // @param {string} param.url WMTS服務網址
  // @param {string} param.identifier WMTS圖層名稱
  terrainview.setBaseLayer({
    url: "BING_MAP",
    identifier: "IMAGE"
  });

  // 設定時間資料
  const list = [
    "0",
    "10",
    "20",
    "30",
    "40",
    "50",
    "60",
    "70",
    "80",
    "90",
    "100"
  ];
  const times = [
    "2023-10-25 00:00:00",
    "2023-10-25 00:00:10",
    "2023-10-25 00:00:20",
    "2023-10-25 00:00:30",
    "2023-10-25 00:00:40",
    "2023-10-25 00:00:50",
    "2023-10-25 00:01:00",
    "2023-10-25 00:01:10",
    "2023-10-25 00:01:20",
    "2023-10-25 00:01:30",
    "2023-10-25 00:01:40"
  ];
  const dates = times.map((t) => new Date(t));

  // 建立時間軸
  const Timeline = new ov.Widget.Timeline({
    view: terrainview,
    startTime: dates[0],
    stopTime: dates[dates.length - 1],
    style: { left: "70px", width: "calc(90% - 70px)" }
  });

  // 建立時間軸播放器
  const TimelinePlayer = new ov.Widget.TimelinePlayer({
    view: terrainview,
    timeline: Timeline,
    playSpeed: 1
  });

  // 建立中心座標
  const center4326 = new GeoPoint(
    119.87433528151684,
    23.069218404158807,
    0.060315079318631545
  );

  // 取得所有流體資料
  Promise.all(
    list.map((s) =>
      getText(
        `https://sample.pilotgaea.com.tw/demo/index/src/testweb/fluid_timeline/Waterflow/Waterflow_t_${s}s.csv`
      )
    )
  ).then((rawData) => {
    const data = rawData.map((datum) => {
      return datum
        .split("\r\n")
        .map((line) => line.split(",").map((l) => l.trim()))
        .filter((l) => !isNaN(parseFloat(l[0])));
    });

    const xs = data.map((datum) =>
      datum.map((values) => parseFloat(values[0]))
    );
    const ys = data.map((datum) =>
      datum.map((values) => parseFloat(values[1]))
    );
    const zs = data.map((datum) =>
      datum.map((values) => parseFloat(values[2]))
    );

    const max = [-Infinity, -Infinity, -Infinity];
    const min = [Infinity, Infinity, Infinity];

    xs.forEach((datum) =>
      datum.forEach((value) => {
        max[0] = Math.max(max[0], value);
        min[0] = Math.min(min[0], value);
      })
    );
    ys.forEach((datum) =>
      datum.forEach((value) => {
        max[1] = Math.max(max[1], value);
        min[1] = Math.min(min[1], value);
      })
    );
    zs.forEach((datum) =>
      datum.forEach((value) => {
        max[2] = Math.max(max[2], value);
        min[2] = Math.min(min[2], value);
      })
    );

    const row = Math.ceil((max[0] - min[0]) * 1);
    const col = Math.ceil((max[1] - min[1]) * 1);
    const level = Math.ceil((max[2] - min[2]) * 1);

    const epsgEngine = GetEPSGEngine();

    const center3857 = new GeoPoint(center4326);
    epsgEngine.Transfer(4326, 3857, center3857);

    const lt = new GeoPoint(min[0], max[1], center3857.z).PlusSelf(center3857);
    const rt = new GeoPoint(max[0], max[1], center3857.z).PlusSelf(center3857);
    const lb = new GeoPoint(min[0], min[1], center3857.z).PlusSelf(center3857);
    const rb = new GeoPoint(max[0], min[1], center3857.z).PlusSelf(center3857);

    epsgEngine.Transfer(3857, 4326, lt);
    epsgEngine.Transfer(3857, 4326, rt);
    epsgEngine.Transfer(3857, 4326, lb);
    epsgEngine.Transfer(3857, 4326, rb);

    const uData = [];
    const vData = [];
    const zData = [];
    for (let i = 0; i < data.length; i++) {
      uData.push(new Array(row * col * level).fill(0));
      vData.push(new Array(row * col * level).fill(0));
      zData.push(new Array(row * col * level).fill(0));
    }

    for (let i = 0; i < data.length; i++) {
      const length = xs[i].length;
      for (let j = 0; j < length; j++) {
        const x = Math.min(
          Math.round(((xs[i][j] - min[0]) / (max[0] - min[0])) * row),
          row
        );
        const y = Math.min(
          Math.round(((max[1] - ys[i][j]) / (max[1] - min[1])) * col),
          col
        );
        const z = Math.min(
          Math.round(((zs[i][j] - min[2]) / (max[2] - min[2])) * level),
          level
        );
        const index = (z * col + y) * row + x;

        uData[i][index] = parseFloat(data[i][j][4]);
        vData[i][index] = parseFloat(data[i][j][5]);
        zData[i][index] = parseFloat(data[i][j][6]);
      }
    }

    // 建立流體資料
    fluidModule.setFluidDataByUVData({
      uData,
      vData,
      zData,
      width: row,
      height: col,
      depth: level,
      lift: -1,
      boundary: new GeoBoundary(
        Math.min(lt.x, lb.x),
        Math.min(lb.y, rb.y),
        Math.max(rt.x, rb.x),
        Math.max(lt.y, rt.y)
      ),
      offset: [
        min[0],
        max[0] - min[0],
        min[1],
        max[1] - min[1],
        min[2],
        max[2] - min[2]
      ],
      speedFactor: 50,
      center: center4326,
      date: dates
    });
    fluidModule.arrowScale = 3;

    // 依數據設定操作面板資料
    document.getElementById("speedFactor").value = fluidModule.speedFactor;
    document.getElementById("minSpeed").value = fluidModule._module.MinSpeed;
    document.getElementById("lift").value = fluidModule.lift;
    document.getElementById("arrowScale").value = fluidModule.arrowScale;

    // 繫結時間軸與流體模組
    Timeline.addLink(fluidModule);
  });
}

// 設定操作面板事件
document.getElementById("minSpeed").addEventListener("input", function (evt) {
  let minSpeed = parseFloat(evt.target.value);
  if (!isNaN(minSpeed)) {
    fluidModule.setFluidDataByParam({ minSpeed });
  }
});

document
  .getElementById("speedFactor")
  .addEventListener("input", function (evt) {
    let speedFactor = parseFloat(evt.target.value);
    if (!isNaN(speedFactor)) {
      fluidModule.speedFactor = speedFactor;
    }
  });

document.getElementById("lift").addEventListener("input", function (evt) {
  let lift = parseFloat(evt.target.value);
  if (!isNaN(lift)) {
    fluidModule.lift = lift;
  }
});

document.getElementById("arrowScale").addEventListener("input", (evt) => {
  let arrowScale = parseFloat(evt.target.value);
  if (!isNaN(arrowScale)) {
    fluidModule.arrowScale = arrowScale;
  }
});

document.getElementById("shape").addEventListener("change", () => {
  let elements = document.getElementsByName("shapeRadio");
  for (let element of elements) {
    if (element.checked) {
      switch (element.value) {
        case "line":
          fluidModule.mode = ov.FLUID_MODE.LINE;
          break;
        case "arrow":
          fluidModule.mode = ov.FLUID_MODE.ARROW;
          break;
      }
    }
  }
});
Copyright © NCHC 2022 Version:13.0 all right reserved,powered by Gitbook修訂時間: 2024-11-20 13:52:09