cesium实现网格化

cesium实现网格化的相关方法

已经封装js 自行调用

javascript 复制代码
/**
 * 三维网格分析 + 无人机仿真工具类
 * 功能:三维网格剖分、航线网格生成、多边形/圆柱/立方体网格、无人机动态仿真
 * 特点:纯功能类,不创建地图,所有数据由外部传入,无任何写死配置
 */
import * as Cesium from 'cesium';

/**
 * 地球半径常量(米)
 */
const M_PER_DEG = 111319.49079327357;

/**
 * 网格剖分各级配置(经纬度步长、行列数)
 * 索引对应网格层级 1~10
 */
const LEVELS = [
  null,
  { lon: 6.0, lat: 4.0, cols: 60, rows: 22 },
  { lon: 0.5, lat: 0.5, cols: 12, rows: 8 },
  { lon: 0.25, lat: 0.25, cols: 2, rows: 2 },
  { lon: 1 / 60, lat: 1 / 60, cols: 15, rows: 15 },
  { lon: 4 / 3600, lat: 4 / 3600, cols: 15, rows: 15 },
  { lon: 2 / 3600, lat: 2 / 3600, cols: 2, rows: 2 },
  { lon: 0.25 / 3600, lat: 0.25 / 3600, cols: 8, rows: 8 },
  { lon: 1 / 32 / 3600, lat: 1 / 32 / 3600, cols: 8, rows: 8 },
  { lon: 1 / 256 / 3600, lat: 1 / 256 / 3600, cols: 8, rows: 8 },
  { lon: 1 / 2048 / 3600, lat: 1 / 2048 / 3600, cols: 8, rows: 8 }
];

/**
 * 最大网格数量限制,防止性能爆炸
 */
const MAX_GRIDS = 50000;

/**
 * 无人机仿真全局状态(单例模式,避免多实例冲突)
 */
const sim = {
  running: false,               // 是否正在运行
  paused: false,                // 是否暂停
  startMs: 0,                   // 开始时间戳
  totalPausedMs: 0,             // 总暂停时长
  pauseStartMs: 0,              // 暂停开始时间
  routeMeters: 0,               // 航线总长度(米)
  segments: [],                 // 航线段列表
  grids: [],                    // 网格数据
  codeToIdx: new Map(),         // 网格编码 → 索引映射
  entities: [],                 // 绘制的实体对象
  drone: null,                  // 无人机实体
  route: null,                  // 航线实体
  alt: 100,                    // 默认飞行高度
  speed: 10,                    // 速度系数
  rate: 5,                      // 速率系数
  level: 7,                     // 网格层级
  currentIdx: -1,               // 当前所在网格索引
  lastPos: null                 // 最后位置(暂停时使用)
};

export default class GridSimulation {
  /**
   * 构造函数
   * @param {Cesium.Viewer} viewer - 外部传入的Cesium地图实例
   */
  constructor(viewer) {
    if (!viewer) throw new Error("必须传入 Cesium Viewer 实例");
    this.map = viewer; // 保存外部传入的viewer
  }

  // ==========================
  // 新增:结果展示函数
  // ==========================
  /**
   * 展示结果(返回HTML文本)
   * @param {String} html - 要展示的内容
   * @returns {String} html文本
   */
  showResult (html) {
    console.log(html);
    return html;
  }

  /**
   * 生成网格列表摘要(最多显示前30条)
   * @param {Array} list - 网格数组
   * @returns {String} 拼接好的HTML摘要
   */
  listSummary (list) {
    let h = '<b>共 ' + list.length + ' 个网格</b><br>';
    list.slice(0, 30).forEach((g, i) => {
      h += i + 1 + '. <code>' + g.code + '</code><br>';
    });
    if (list.length > 30) h += '... 仅显示前 30 条';
    return h;
  }

  /**
   * 绘制单点网格
   * @param {Object} bounds - 网格范围 {min_lon,min_lat,max_lon,max_lat,min_alt,max_alt}
   * @returns {Object} { list: 网格数组, html: 展示文本 }
   */
  drawPointGrid (bounds) {
    this.clearAll();
    this.drawGrid(bounds, Cesium.Color.YELLOW.withAlpha(0.5));
    this.flyToGrids([bounds]);

    // 构造返回数据
    const list = [bounds];
    const html = this.showResult(this.listSummary(list));
    return { list, html };
  }

  /**
   * 绘制航线网格
   * @param {Object} start - 起点 {lat,lon,alt}
   * @param {Object} end - 终点 {lat,lon,alt}
   * @param {number} level - 网格层级
   * @returns {Object} { list: 网格数组, html: 展示文本 }
   */
  drawLineGrid (start, end, level = 7) {
    this.clearAll();
    const list = this.queryLineGrids(start, end, level);
    list.forEach(g => this.drawGrid(g, Cesium.Color.LIME.withAlpha(0.35)));
    this.map.entities.add({
      polyline: {
        positions: Cesium.Cartesian3.fromDegreesArrayHeights([
          start.lon, start.lat, start.alt,
          end.lon, end.lat, end.alt
        ]),
        width: 3,
        material: Cesium.Color.RED
      }
    });
    this.flyToGrids(list);

    const html = this.showResult(this.listSummary(list));
    return { list, html };
  }

  /**
   * 绘制圆柱范围网格
   * @param {Object} center - 中心点 {lat,lon}
   * @param {number} radius - 半径(米)
   * @param {number} minAlt - 最低高度
   * @param {number} maxAlt - 最高高度
   * @param {number} level - 网格层级
   * @returns {Object} { list: 网格数组, html: 展示文本 }
   */
  drawCylinderGrid (center, radius, minAlt, maxAlt, level = 7) {
    this.clearAll();
    const list = this.queryCylinderGrids(center, radius, minAlt, maxAlt, level);
    list.forEach(g => this.drawGrid(g, Cesium.Color.ORANGE.withAlpha(0.35)));
    this.map.entities.add({
      position: Cesium.Cartesian3.fromDegrees(center.lon, center.lat, (minAlt + maxAlt) / 2),
      cylinder: {
        length: maxAlt - minAlt,
        topRadius: radius,
        bottomRadius: radius,
        material: Cesium.Color.RED.withAlpha(0.12),
        outline: true,
        outlineColor: Cesium.Color.RED
      }
    });
    this.flyToGrids(list);

    const html = this.showResult(this.listSummary(list));
    return { list, html };
  }

  /**
   * 绘制多边形范围网格
   * @param {Array} points - 点数组 [{lat,lon},...]
   * @param {number} minAlt - 最低高度
   * @param {number} maxAlt - 最高高度
   * @param {number} level - 网格层级
   * @returns {Object} { list: 网格数组, html: 展示文本 }
   */
  drawPrismGrid (points, minAlt, maxAlt, level = 7) {
    this.clearAll();
    const list = this.queryPrismGrids(points, minAlt, maxAlt, level);
    list.forEach(g => this.drawGrid(g, Cesium.Color.MAGENTA.withAlpha(0.35)));
    const arr = [];
    points.forEach(p => arr.push(p.lon, p.lat, maxAlt));
    arr.push(points[0].lon, points[0].lat, maxAlt);
    this.map.entities.add({
      polyline: {
        positions: Cesium.Cartesian3.fromDegreesArrayHeights(arr),
        width: 3,
        material: Cesium.Color.RED
      }
    });
    this.flyToGrids(list);

    const html = this.showResult(this.listSummary(list));
    return { list, html };
  }

  /**
   * 绘制立方体范围网格
   * @param {number} minLat 
   * @param {number} maxLat 
   * @param {number} minLon 
   * @param {number} maxLon 
   * @param {number} minAlt 
   * @param {number} maxAlt 
   * @param {number} level 
   * @returns {Object} { list: 网格数组, html: 展示文本 }
   */
  drawCuboidGrid (minLat, maxLat, minLon, maxLon, minAlt, maxAlt, level = 7) {
    this.clearAll();
    const list = this.queryCuboidGrids(minLat, maxLat, minLon, maxLon, minAlt, maxAlt, level);
    list.forEach(g => this.drawGrid(g, Cesium.Color.BLUE.withAlpha(0.35)));
    this.map.entities.add({
      rectangle: {
        coordinates: Cesium.Rectangle.fromDegrees(minLon, minLat, maxLon, maxLat),
        material: Cesium.Color.RED.withAlpha(0.12),
        outline: true,
        outlineColor: Cesium.Color.RED,
        height: minAlt,
        extrudedHeight: maxAlt
      }
    });
    this.flyToGrids(list);

    const html = this.showResult(this.listSummary(list));
    return { list, html };
  }

  /**
   * 启动无人机仿真
   * @param {Array} routePoints - 航线点位 [{lat,lon},...]
   * @param {Object} options - 配置 {alt,speed,rate,level}
   * @param {Function} onStatusUpdate - 实时状态回调函数
   */
  startSimulation (routePoints, options = {}, onStatusUpdate = null) {
    if (!this.map) return;
    // 恢复暂停
    if (sim.paused) {
      sim.paused = false;
      sim.totalPausedMs += performance.now() - sim.pauseStartMs;
      requestAnimationFrame(() => this.simTick(onStatusUpdate));
      return;
    }
    if (sim.running) return;

    // 外部传入配置
    const { alt = 100, speed = 10, rate = 5, level = 7 } = options;
    sim.alt = alt;
    sim.speed = speed;
    sim.rate = rate;
    sim.level = level;
    sim.lastPos = { ...routePoints[0], alt };

    this.resetSim();
    const pts = routePoints;
    if (pts.length < 2) {
      alert('航线至少需要2个点');
      return;
    }

    // 构建航线段
    let cum = 0;
    for (let i = 0; i < pts.length - 1; i++) {
      const a = pts[i];
      const b = pts[i + 1];
      const d = this.distM(a.lat, a.lon, b.lat, b.lon);
      sim.segments.push({ a, b, dist: d, start: cum });
      cum += d;
    }
    sim.routeMeters = cum;

    // 生成网格
    const map = new Map();
    for (const seg of sim.segments) {
      const list = this.queryLineGrids(
        { lat: seg.a.lat, lon: seg.a.lon, alt: sim.alt },
        { lat: seg.b.lat, lon: seg.b.lon, alt: sim.alt },
        sim.level
      );
      for (const g of list) if (!map.has(g.code)) map.set(g.code, g);
    }
    sim.grids = [...map.values()];
    sim.grids.forEach((g, i) => sim.codeToIdx.set(g.code, i));

    // 绘制网格
    sim.grids.forEach(g => {
      const e = this.map.entities.add({
        rectangle: {
          coordinates: Cesium.Rectangle.fromDegrees(g.min_lon, g.min_lat, g.max_lon, g.max_lat),
          material: Cesium.Color.LIME.withAlpha(0.35),
          outline: true,
          outlineColor: Cesium.Color.WHITE,
          height: g.min_alt,
          extrudedHeight: g.max_alt
        }
      });
      sim.entities.push(e);
    });

    // 绘制航线
    const arr = [];
    pts.forEach(p => arr.push(p.lon, p.lat, sim.alt));
    sim.route = this.map.entities.add({
      polyline: {
        positions: Cesium.Cartesian3.fromDegreesArrayHeights(arr),
        width: 3,
        material: Cesium.Color.YELLOW,
        clampToGround: false
      }
    });

    // 绘制无人机(动态点位)
    sim.drone = this.map.entities.add({
      position: new Cesium.CallbackProperty(() => {
        const p = this.computeDronePos();
        return Cesium.Cartesian3.fromDegrees(p.lon, p.lat, p.alt);
      }, false),
      point: {
        pixelSize: 14,
        color: Cesium.Color.RED,
        outlineColor: Cesium.Color.WHITE,
        outlineWidth: 2,
        disableDepthTestDistance: Number.POSITIVE_INFINITY
      },
      label: {
        text: '🚁 UAV',
        font: '14px sans-serif',
        fillColor: Cesium.Color.YELLOW,
        style: Cesium.LabelStyle.FILL_AND_OUTLINE,
        outlineWidth: 2,
        outlineColor: Cesium.Color.BLACK,
        pixelOffset: new Cesium.Cartesian2(0, -22),
        disableDepthTestDistance: Number.POSITIVE_INFINITY
      }
    });

    // 相机飞行
    this.flyToGrids(sim.grids);
    sim.running = true;
    sim.startMs = performance.now();
    sim.totalPausedMs = 0;
    requestAnimationFrame(() => this.simTick(onStatusUpdate));
  }

  /**
   * 暂停仿真
   */
  pauseSim () {
    if (!sim.running || sim.paused) return;
    const currentPos = this.computeDronePos();
    if (currentPos) sim.lastPos = currentPos;
    sim.paused = true;
    sim.pauseStartMs = performance.now();
  }

  /**
   * 重置仿真 + 清空所有实体
   */
  resetSim () {
    sim.running = false;
    sim.paused = false;
    sim.routeMeters = 0;
    sim.segments = [];
    sim.grids = [];
    sim.codeToIdx = new Map();
    sim.entities = [];
    sim.drone = null;
    sim.route = null;
    sim.currentIdx = -1;
    if (this.map) this.map.entities.removeAll();
  }

  // ==================== 核心算法 ====================
  /**
   * 计算无人机实时位置
   */
  computeDronePos () {
    if (sim.paused || !sim.running) return sim.lastPos;
    const elapsed = performance.now() - sim.startMs - sim.totalPausedMs;
    const travelled = Math.min(sim.routeMeters, (elapsed / 1000) * sim.speed * sim.rate);
    let s = sim.segments[sim.segments.length - 1];
    for (const seg of sim.segments) {
      if (travelled <= seg.start + seg.dist) {
        s = seg;
        break;
      }
    }
    const t = s.dist > 0 ? (travelled - s.start) / s.dist : 0;
    const pos = {
      lat: s.a.lat + t * (s.b.lat - s.a.lat),
      lon: s.a.lon + t * (s.b.lon - s.a.lon),
      alt: sim.alt,
      travelled
    };
    sim.lastPos = pos;
    return pos;
  }

  /**
   * 仿真帧更新(支持实时状态回调)
   */
  simTick (onStatusUpdate) {
    if (!sim.running || sim.paused) return;
    const p = this.computeDronePos();
    const code = this.toCode(this.encode(p.lat, p.lon, p.alt, sim.level));
    const idx = sim.codeToIdx.has(code) ? sim.codeToIdx.get(code) : sim.currentIdx;

    // 实时状态数据
    const status = {
      travelled: p.travelled,               // 【已飞行距离】
      totalDistance: sim.routeMeters,        // 【航线总长度】
      percent: (p.travelled / sim.routeMeters) * 100,  // 【飞行进度百分比】
      lat: p.lat,                            // 【无人机当前纬度】
      lon: p.lon,                            // 【无人机当前经度】
      alt: p.alt,                            // 【无人机当前高度】
      currentGridCode: code,                 // 【当前所在的网格编码】
      currentGridIndex: idx,                 // 【当前网格在列表里的序号】
      passedGrids: idx,                     // 【已经飞过多少个网格】
      totalGrids: sim.grids.length,          // 【整条航线一共有多少网格】
      remainingGrids: sim.grids.length - idx - 1  // 【还剩多少网格没飞】
    };

    // 回调返回状态
    if (typeof onStatusUpdate === 'function') {
      onStatusUpdate(status);
    }

    // 网格高亮更新
    if (idx !== sim.currentIdx) {
      sim.entities.forEach((e, i) => {
        let c = i < idx ? Cesium.Color.BLUE.withAlpha(0.4)
          : i === idx ? Cesium.Color.RED.withAlpha(0.6)
            : Cesium.Color.LIME.withAlpha(0.35);
        e.rectangle.material = c;
      });
      sim.currentIdx = idx;
    }

    // 到达终点
    if (p.travelled >= sim.routeMeters) {
      sim.entities.forEach(e => { e.rectangle.material = Cesium.Color.BLUE.withAlpha(0.4) });
      sim.running = false;
      return;
    }

    requestAnimationFrame(() => this.simTick(onStatusUpdate));
  }

  /**
   * 多边形网格生成
   */
  queryPrismGrids (poly, bottomAlt, topAlt, level) {
    let minLat = Infinity, maxLat = -Infinity, minLon = Infinity, maxLon = -Infinity;
    poly.forEach(p => {
      minLat = Math.min(minLat, p.lat);
      maxLat = Math.max(maxLat, p.lat);
      minLon = Math.min(minLon, p.lon);
      maxLon = Math.max(maxLon, p.lon);
    });
    const all = this.queryCuboidGrids(minLat, maxLat, minLon, maxLon, bottomAlt, topAlt, level);
    return all.filter(g => this.pointInPoly(g.center.lat, g.center.lon, poly) && g.center.alt >= bottomAlt && g.center.alt <= topAlt);
  }

  /**
   * 射线法判断点是否在多边形内
   */
  pointInPoly (lat, lon, poly) {
    let inside = false;
    for (let i = 0, j = poly.length - 1; i < poly.length; j = i++) {
      const xi = poly[i].lon, yi = poly[i].lat, xj = poly[j].lon, yj = poly[j].lat;
      if (yi > lat !== yj > lat && lon < ((xj - xi) * (lat - yi)) / (yj - yi + 1e-18) + xi) inside = !inside;
    }
    return inside;
  }

  /**
   * 圆柱范围网格
   */
  queryCylinderGrids (center, radius, bottomAlt, topAlt, level) {
    const dLat = radius / M_PER_DEG;
    const dLon = radius / (M_PER_DEG * Math.cos((center.lat * Math.PI) / 180));
    const all = this.queryCuboidGrids(center.lat - dLat, center.lat + dLat, center.lon - dLon, center.lon + dLon, bottomAlt, topAlt, level);
    return all.filter(g => this.distM(center.lat, center.lon, g.center.lat, g.center.lon) <= radius && g.center.alt >= bottomAlt && g.center.alt <= topAlt);
  }

  /**
   * 立方体范围网格(基础网格生成)
   */
  queryCuboidGrids (minLat, maxLat, minLon, maxLon, minAlt, maxAlt, level) {
    const L = LEVELS[level];
    const dLat = L.lat * 0.999, dLon = L.lon * 0.999, dAlt = L.lat * M_PER_DEG * 0.999;
    const map = new Map();
    const add = (p) => {
      const g = this.encode(p[0], p[1], p[2], level);
      const k = this.keyOf(g);
      if (!map.has(k)) map.set(k, this.gridInfo(g));
    };
    for (let la = minLat; la <= maxLat + 1e-12; la += dLat)
      for (let lo = minLon; lo <= maxLon + 1e-12; lo += dLon)
        for (let al = minAlt; al <= maxAlt + 1e-12; al += dAlt) {
          add([Math.min(la, maxLat), Math.min(lo, maxLon), Math.min(al, maxAlt)]);
          if (map.size > MAX_GRIDS) throw new Error('网格数量超出限制');
        }
    add([maxLat, maxLon, maxAlt]);
    return [...map.values()];
  }

  /**
   * 两点间距离(米)
   */
  distM (lat1, lon1, lat2, lon2) {
    const R = 6371008.8;
    const dLat = ((lat2 - lat1) * Math.PI) / 180;
    const dLon = ((lon2 - lon1) * Math.PI) / 180;
    const a = Math.sin(dLat / 2) ** 2 + Math.cos((lat1 * Math.PI) / 180) * Math.cos((lat2 * Math.PI) / 180) * Math.sin(dLon / 2) ** 2;
    return 2 * R * Math.asin(Math.sqrt(a));
  }

  /**
   * 绘制单个网格
   */
  drawGrid (g, color) {
    this.map.entities.add({
      rectangle: {
        coordinates: Cesium.Rectangle.fromDegrees(g.min_lon, g.min_lat, g.max_lon, g.max_lat),
        material: color || Cesium.Color.CYAN.withAlpha(0.35),
        outline: true,
        outlineColor: Cesium.Color.WHITE,
        height: g.min_alt,
        extrudedHeight: g.max_alt
      }
    });
  }

  /**
   * 相机飞行到网格区域
   */
  flyToGrids (list) {
    if (!list.length) return;
    let mnLat = 90, mxLat = -90, mnLon = 180, mxLon = -180;
    list.forEach(g => {
      mnLat = Math.min(mnLat, g.max_lat);
      mxLat = Math.max(mxLat, g.min_lat);
      mnLon = Math.min(mnLon, g.min_lon);
      mxLon = Math.max(mxLon, g.max_lon);
    });
    this.map.camera.flyTo({
      destination: Cesium.Rectangle.fromDegrees(mnLon, mnLat, mxLon, mxLat),
      duration: 1
    });
  }

  /**
   * 航线网格生成
   */
  queryLineGrids (a, b, level) {
    const L = LEVELS[level];
    const step = Math.min(L.lat, L.lon) * 0.5;
    const dLat = b.lat - a.lat, dLon = b.lon - a.lon, dAlt = b.alt - a.alt;
    const dist = this.distM(a.lat, a.lon, b.lat, b.lon);
    const steps = Math.max(1, Math.ceil(dist / step) + 1);
    const map = new Map();
    for (let i = 0; i <= steps; i++) {
      const t = i / steps;
      const g = this.encode(a.lat + t * dLat, a.lon + t * dLon, a.alt + t * dAlt, level);
      const k = this.keyOf(g);
      if (!map.has(k)) map.set(k, this.gridInfo(g));
      if (map.size > MAX_GRIDS) throw new Error('网格数量超出限制');
    }
    return [...map.values()];
  }

  /**
   * 高度步长
   */
  altSize (lvl) { return LEVELS[lvl].lat * M_PER_DEG; }

  /**
   * 数字转36进制(编码用)
   */
  b36 (n) { return n.toString(36).toUpperCase(); }

  /**
   * 网格编码解码 → 经纬度范围
   */
  decodeBox (g) {
    let lon0 = -180 + g.lonZone * 6;
    let latAbs0 = g.latBand * 4;
    let altAbs0 = g.altIdx[0] * this.altSize(1);
    for (let i = 0; i < g.cols.length; i++) {
      const L = LEVELS[i + 2];
      lon0 += g.cols[i] * L.lon;
      latAbs0 += g.rows[i] * L.lat;
      altAbs0 += g.altIdx[i + 1] * L.lat * M_PER_DEG;
    }
    const Llast = LEVELS[g.level];
    const lonMin = lon0, lonMax = lon0 + Llast.lon;
    let latMin, latMax;
    if (g.hemi === 'N') {
      latMin = latAbs0;
      latMax = latAbs0 + Llast.lat;
    } else {
      latMin = -(latAbs0 + Llast.lat);
      latMax = -latAbs0;
    }
    let altMin, altMax;
    if (g.altSign === 0) {
      altMin = altAbs0;
      altMax = altAbs0 + Llast.lat * M_PER_DEG;
    } else {
      altMin = -(altAbs0 + Llast.lat * M_PER_DEG);
      altMax = -altAbs0;
    }
    return {
      min_lat: latMin,
      max_lat: latMax,
      min_lon: lonMin,
      max_lon: lonMax,
      min_alt: altMin,
      max_alt: altMax
    };
  }

  /**
   * 获取网格完整信息
   */
  gridInfo (g) {
    const b = this.decodeBox(g);
    return Object.assign({
      code: this.toCode(g),
      level: g.level,
      center: {
        lat: (b.min_lat + b.max_lat) / 2,
        lon: (b.min_lon + b.max_lon) / 2,
        alt: (b.min_alt + b.max_alt) / 2
      }
    }, b);
  }

  /**
   * 网格编码转字符串
   */
  toCode (g) {
    let s = g.hemi + String(g.lonZone + 1).padStart(2, '0') + String.fromCharCode(65 + g.latBand);
    for (let i = 0; i < g.cols.length; i++)
      s += '-' + this.b36(g.rows[i]) + this.b36(g.cols[i]);
    s += ' / H' + g.altSign;
    for (let i = 0; i < g.altIdx.length; i++)
      s += '-' + this.b36(g.altIdx[i]);
    return s;
  }

  /**
   * 获取网格唯一标识
   */
  keyOf (g) { return this.toCode(g); }

  /**
   * 经纬度高度 → 网格编码
   */
  encode (lat, lon, alt, level) {
    const hemi = lat >= 0 ? 'N' : 'S';
    const absLat = Math.abs(lat);
    let lonZone = Math.floor((lon + 180) / 6);
    if (lonZone === 60) lonZone = 59;
    const latBand = Math.min(21, Math.floor(absLat / 4));
    let lon0 = -180 + lonZone * 6;
    let lat0 = latBand * 4;
    let remLon = lon - lon0;
    let remLat = absLat - lat0;
    const altSign = alt >= 0 ? 0 : 1;
    let absAlt = Math.abs(alt);
    const alt1 = Math.floor(absAlt / this.altSize(1));
    let remAlt = absAlt - alt1 * this.altSize(1);
    const cols = [], rows = [], altIdx = [alt1];
    for (let i = 2; i <= level; i++) {
      const L = LEVELS[i];
      let c = Math.floor(remLon / L.lon);
      let r = Math.floor(remLat / L.lat);
      let a = Math.floor(remAlt / (L.lat * M_PER_DEG));
      if (c >= L.cols) c = L.cols - 1;
      if (r >= L.rows) r = L.rows - 1;
      if (a < 0) a = 0;
      cols.push(c);
      rows.push(r);
      altIdx.push(a);
      remLon -= c * L.lon;
      remLat -= r * L.lat;
      remAlt -= a * L.lat * M_PER_DEG;
    }
    return { hemi, lonZone, latBand, cols, rows, altSign, altIdx, level };
  }

  /**
   * 清空所有实体
   */
  clearAll () { this.map.entities.removeAll(); }
}
javascript 复制代码
<template>
  <div id="map" class="map">
    <div class="data">
      <button @click="drawPoint">坐标点网格</button>
      <button @click="drawLine">航线网格</button>
      <button @click="drawCylinder">圆柱网格</button>
      <button @click="drawPrism">多边形网格</button>
      <button @click="drawCuboid">立方体网格</button>

      <div style="margin-top:10px;">
        <button @click="startSim">▶ 开始仿真</button>
        <button @click="s.pauseSim()">⏸ 暂停</button>
        <button @click="s.resetSim()">⏹ 重置</button>
      </div>
    </div>
  </div>
</template>

<script>
window.CESIUM_BASE_URL = '/';
import * as Cesium from 'cesium';
import 'cesium/Build/Cesium/Widgets/widgets.css';
import GridSimulation from './gridSimulation';

export default {
  mounted () {
    this.initViewer();
  },

  methods: {
    async initViewer () {
      Cesium.Ion.defaultAccessToken = ''; // 设置 Cesium Ion 访问令牌
      const viewer = new Cesium.Viewer('map', {
        terrain: Cesium.Terrain.fromWorldTerrain(), // 使用全球地形
        homeButton: false, // 不显示首页按钮
        sceneModePicker: true, // 显示场景模式切换器
        baseLayerPicker: false, // 隐藏底图切换器
        animation: false, // 不显示动画控件
        infoBox: false, // 隐藏要素信息框
        selectionIndicator: false, // 隐藏选取指示器
        geocoder: false, // 隐藏地名搜索控件
        timeline: true, // 临时显示时间线控件
        fullscreenButton: false, // 不显示全屏按钮
        shouldAnimate: false, // 不启用场景动画
        navigationHelpButton: false, // 隐藏导航帮助按钮
        selectionIndicator: false, // 再次隐藏选取指示器
        timeline: false, // 隐藏时间线控件


      });
      viewer.cesiumWidget.creditContainer.style.display = 'none' // 隐藏 Cesium 版权信息显示
      viewer.camera.flyTo({
        destination: Cesium.Cartesian3.fromDegrees(116.3975, 39.9087, 3000.0), // 飞到指定经纬度位置
        orientation: {
          heading: Cesium.Math.toRadians(0.0), // 方向角为 0
          pitch: Cesium.Math.toRadians(-45.0), // 俯仰角 -45 度
          roll: 0.0 // 侧倾角 0
        },
        duration: 0 // 立即跳转,无动画
      });

      // ===== 传入 viewer =====
      this.s = new GridSimulation(viewer);
    },

    // ===== 所有数据自己定义,外部传入 =====
    drawPoint () {
      const bounds = {
        min_lon: 116.39743,
        min_lat: 39.90868,
        max_lon: 116.3975,
        max_lat: 39.90875,
        min_alt: 100,
        max_alt: 120,
      };
      let res = this.s.drawPointGrid(bounds);
      console.log("🚀 ~ res:", res)
    },

    drawLine () {
      const start = { lat: 39.9087, lon: 116.3975, alt: 120 };
      const end = { lat: 39.912, lon: 116.402, alt: 200 };
      let res = this.s.drawLineGrid(start, end, 7);
      console.log("🚀 ~ res:", res);
    },

    drawCylinder () {
      const center = { lat: 39.9087, lon: 116.3975 };
      let res = this.s.drawCylinderGrid(center, 100, 0, 100, 7);
      console.log("🚀 ~ res:", res);
    },

    drawPrism () {
      const points = [
        { lat: 39.9087, lon: 116.3975 },
        { lat: 39.909, lon: 116.398 },
        { lat: 39.9085, lon: 116.399 },
      ];
      let res = this.s.drawPrismGrid(points, 0, 100, 7);
      console.log("🚀 ~ res:", res);
    },

    drawCuboid () {
      let res = this.s.drawCuboidGrid(39.9086, 39.9088, 116.3974, 116.3976, 0, 100, 7);
      console.log("🚀 ~ res:", res);
    },

    startSim () {
      const route = [
        { lat: 39.9080, lon: 116.3960 },
        { lat: 39.9095, lon: 116.3980 },
        { lat: 39.9110, lon: 116.4000 },
      ];
      const options = { alt: 100, speed: 10, level: 7 };
      // let res = this.s.startSimulation(route, options); 
      this.s.startSimulation(route, options, (status) => {
        console.log(status) // 你要的所有实时数据
      })
      // console.log("🚀 ~ res:", res);
    },
  },
};
</script>

<style scoped>
.map {
  width: 100%;
  height: 100vh;
}

.data {
  position: absolute;
  top: 10px;
  left: 10px;
  background: rgba(255, 255, 255, 0.8);
  z-index: 1000;
  padding: 10px;
}
</style>
相关推荐
游乐码14 小时前
Unity基础(十二)资源异步加载
unity·游戏引擎
weixin_4419400116 小时前
vuforia ar unity实验教程
unity·游戏引擎·ar
妙为18 小时前
unreal engine5(UE5)中使用Rider
ue5·游戏引擎·虚幻·rider
诙_21 小时前
unity——C#
unity·c#·游戏引擎
晓13131 天前
【Cocos Creator 3.x】篇——第一章 简介
前端·javascript·游戏引擎
晓13131 天前
【Cocos Creator 2.x】篇——第五章 游戏常用关键技术
前端·javascript·vue.js·游戏引擎
caimouse1 天前
2D 与 3D 跨平台游戏引擎
游戏引擎
游乐码1 天前
Unity基础(十三)资源卸载
unity·游戏引擎
冰糖橘子ABC1 天前
Unity 动作重定向
unity·游戏引擎