Vue3项目-Cesium地图初始化

一、安装、配置Cesium

1、安装Cesium
npm install cesium
2、文件拷贝

        将node_modules中cesium/Build/Cesium文件夹拷贝至public目录下

3、index.html配置
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8" />
  <link rel="icon" type="image/svg+xml" href="/vite.svg" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>Vite + Vue</title>
  <link rel="stylesheet" href="./public/Cesium/Widgets/widgets.css">
</head>

<body>
  <div id="app"></div>
  <script type="module" src="/src/main.js"></script>
  <script type="text/javascript" src="./public/Cesium/Cesium.js"></script>
</body>

</html>

二、使用Cesium

1、HTML
<template>
  <div class="cesium_map">
    <!-- cesium的DOM -->
    <div id="cesiumContainer"></div>
    <!-- 经纬度 -->
    <div class="cesium_position">
      <div class="position_lonlat">
        <p>经度:</p>
        <p>{{ location.lon.toFixed(6) }}</p>
      </div>
      <div class="position_lonlat">
        <p>纬度:</p>
        <p>{{ location.lat.toFixed(6) }}</p>
      </div>
    </div>
  </div>
</template>
2、JavaScript
<script setup>
import { reactive, ref, onMounted } from "vue";

// ---------------------------<<数据>>---------------------------
// 鼠标当前位置信息显示
const location = reactive({
  lon: 0,
  lat: 0,
  height: 0,
});

// 点位数组
const pointArr = ref([
  {
    lon: 123.645025,
    lat: 22.03158,
    alt: 300,
    time: null,
  },
  {
    lon: 123.647785,
    lat: 22.02946,
    alt: 300,
    time: null,
  },
  {
    lon: 123.651688,
    lat: 22.02946,
    alt: 300,
    time: null,
  },
  {
    lon: 123.654448,
    lat: 22.03158,
    alt: 300,
    time: null,
  },
  {
    lon: 123.654448,
    lat: 22.03519,
    alt: 300,
    time: null,
  },
  {
    lon: 123.651688,
    lat: 22.03754,
    alt: 300,
    time: null,
  },
  {
    lon: 123.647785,
    lat: 22.03754,
    alt: 300,
    time: null,
  },
  {
    lon: 123.645025,
    lat: 22.03519,
    alt: 300,
    time: null,
  },
  {
    lon: 123.645025,
    lat: 22.03158,
    alt: 300,
    time: null,
  },
]);

// 侧向线计时器
const lineTimer = ref(null);

// 侧向线路径
const aggregate = ref(
  Cesium.Cartesian3.fromDegreesArrayHeights([
    123.649379, 22.033591, 0, 123.645025, 22.03158, 300,
  ])
);

// ---------------------------<<函数>>---------------------------
function initMap() {
  // 在线地图token
  Cesium.Ion.defaultAccessToken = "map_token";

  // 在线地图
  let imageryProvider = new Cesium.ArcGisMapServerImageryProvider({
    url: "https://services.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer",
  });

  var viewer = new Cesium.Viewer("cesiumContainer", {
    sceneMode: Cesium.SceneMode.SCENE3D, //初始场景模式 为二维
    fullscreenButton: false,
    homeButton: false,
    scene3DOnly: false,
    creditContainer: "cesiumContainer",
    vrButton: false,
    skyAtmosphere: false,
    infoBox: false, //是否显示点击要素之后显示的信息
    imageryProvider: imageryProvider,
    maximumScreenSpaceError: 16, //默认值16 用于提高细节细化级别的最大屏幕空间错误
    animation: false, //是否显示动画控件 是否显示图层选择控件
    geocoder: false, //是否显示地名查找控件
    timeline: false, //是否显示时间线控件
    sceneModePicker: true, //是否显示投影方式控件
    navigationHelpButton: false, //是否显示帮助信息控件
    shadows: true,
    shouldAnimate: true,
    baseLayerPicker: false, //是否显示图层选择控件
    selectionIndicator: false, //设置绿色框框不可见
    skyBox: false,
    contextOption: {
      webgl: {
        alpha: true,
      },
    },
  });
  viewer.scene.globe.enableLighting = false;
  viewer.shadows = false;

  viewer.sceneModePicker.viewModel.duration = 0;
  viewer.scene.globe.enableLighting = false;
  Cesium.Camera.DEFAULT_VIEW_FACTOR = 0.2; // 摄像机到地图的距离放大倍数
  viewer.camera.flyHome(0);

  //取消双击事件
  viewer.cesiumWidget.screenSpaceEventHandler.removeInputAction(
    Cesium.ScreenSpaceEventType.LEFT_DOUBLE_CLICK
  );
  // 调整画面精细度
  viewer.resolutionScale = 1; //默认值为1.0
  viewer.scene.backgroundColor = Cesium.Color.TRANSPARENT;
  viewer.scene.globe.baseColor = Cesium.Color.TRANSPARENT;
  //是否开启抗锯齿
  viewer.scene.fxaa = false;
  viewer.scene.postProcessStages.fxaa.enabled = false;
  viewer.scene.globe.showGroundAtmosphere = true;
  //指北针
  var options = {};
  options.enableCompass = true; //罗盘
  options.enableZoomControls = true; //缩放
  options.enableDistanceLegend = true; //比例尺
  options.enableCompassOuterRing = true; //指南针外环
  // viewer.extend(CesiumNavigation, options);
  window.viewer = viewer;
  // 设置相机位置
  viewer.scene.camera.setView({
    destination: Cesium.Cartesian3.fromDegrees(123.652734, 22.032279, 3000),
    orientation: {
      heading: Cesium.Math.toRadians(0),
      pitch: Cesium.Math.toRadians(-90),
      roll: 0.0,
    },
  });

  // 开启画面帧频
  // viewer.scene.debugShowFramesPerSecond = true;

  // 鼠标事件
  var handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas);
  // 移动事件
  handler.setInputAction((movement) => {
    if (movement.endPosition) {
      //获取点击位置的经纬度
      getMovementPositiond(movement.endPosition, (position) => {
        location.lon = position.lon;
        location.lat = position.lat;
      });
    }
  }, Cesium.ScreenSpaceEventType.MOUSE_MOVE);
  // 滚轮事件
  handler.setInputAction((movement) => {
    console.log("滚轮事件");
  }, Cesium.ScreenSpaceEventType.WHEEL);
  //  双击事件
  handler.setInputAction((movement) => {
    console.log("双击事件");
  }, Cesium.ScreenSpaceEventType.LEFT_DOUBLE_CLICK);
  // 单击事件
  handler.setInputAction((movement) => {
    console.log("单击事件");
  }, Cesium.ScreenSpaceEventType.LEFT_CLICK);
}

// 坐标转换经纬度
function getMovementPositiond(position, callback) {
  var viewer = window.viewer;
  var scene = viewer.scene;
  var ellipsoid = scene.globe.ellipsoid;
  var cartesian = scene.camera.pickEllipsoid(position, ellipsoid);
  if (!cartesian) return; //鼠标在地球外
  //将笛卡尔坐标转换为地理坐标
  var cartographic = ellipsoid.cartesianToCartographic(cartesian);
  //将弧度转为度的十进制度表示
  var lon = Cesium.Math.toDegrees(cartographic.longitude);
  var lat = Cesium.Math.toDegrees(cartographic.latitude);
  var cameraAlt = ellipsoid.cartesianToCartographic(
    viewer.camera.position
  ).height;
  var heading = Cesium.Math.toDegrees(viewer.camera.heading);
  //倾斜角度   围绕Y轴旋转
  var pitch = Cesium.Math.toDegrees(viewer.camera.pitch);
  //围绕X轴旋转
  var roll = Cesium.Math.toDegrees(viewer.camera.roll);
  let ret = {
    lon,
    lat,
    // alt,
    camera: { alt: cameraAlt, heading, pitch, roll },
  };
  callback && callback(ret);
  return ret;
}

// 创建点位
function createPoint(ps) {
  viewer.entities.add({
    position: Cesium.Cartesian3.fromDegrees(ps.lon, ps.lat, ps.alt),
    point: {
      pixelSize: 5,
      // 点位颜色,fromCssColorString 可以直接使用CSS颜色
      color: Cesium.Color.fromCssColorString("#ee0000"),
      // 边框颜色
      outlineColor: Cesium.Color.fromCssColorString("#fff"),
      // 边框宽度(像素)
      outlineWidth: 2,
      // 显示在距相机的距离处的属性,多少区间内是可以显示的
      distanceDisplayCondition: new Cesium.DistanceDisplayCondition(
        0,
        15000000
      ),
    },
  });
}
// 创建线条
function createLine(ps) {
  // 航迹线id
  let id = `line`;

  viewer.entities.add({
    id, //  模型id
    name: "line", // 所属的父级id
    // polyline 折线
    polyline: {
      // 参数依次为[经度1, 纬度1, 高度1, 经度2, 纬度2, 高度2]
      positions: Cesium.Cartesian3.fromDegreesArrayHeights(ps),
      // 注:线条起止,可以获取鼠标点击位置的经纬度进行线条绘制

      // 宽度
      width: 2,

      // 线的颜色
      material: Cesium.Color.fromCssColorString("tomato"), //  "tomato"

      clampToGround: false, // 不紧贴地面

      // 线的顺序,仅当`clampToGround`为true并且支持地形上的折线时才有效。
      zIndex: 999,

      // 显示在距相机的距离处的属性,多少区间内是可以显示的
      distanceDisplayCondition: new Cesium.DistanceDisplayCondition(
        0,
        15000000
      ),

      // 是否显示
      show: true,
    },
  });
}
// 绘制圆锥
function createCone(info) {
  if (!info.angel && info.angel != 0) {
    console.error("请传入圆锥方位");
    return;
  }

  let {
    lon, // 经度
    lat, // 纬度
    alt, // 高度
    length, // 圆锥高度
    angel,
    pitch,
  } = info;

  // 计算角度
  let params = {
    lon, // 经度
    lat, // 纬度
    alt, // 高度
    length, // 圆锥高度
    startAz: angel - 5, //起始方位角
    endAz: angel + 5, //终止方位角
    startEl: pitch - 5, //起始俯仰角
    endEl: pitch + 5, //终止俯仰角
  };

  var result = calcBeamScaleMatrix4(params);

  //绘制圆锥几何体
  var cone = new Cesium.Primitive({
    show: true,
    releaseGeometryInstances: false, // 默认为true,自动清空geometryInstances配置
    geometryInstances: new Cesium.GeometryInstance({
      id: "cone",
      name: "cone",
      geometry: new Cesium.CylinderGeometry({
        length: 200,
        topRadius: 0.0,
        bottomRadius: result.bottomRadius,
      }),
    }),
    modelMatrix: result.mm, //提供位置与姿态参数
    appearance: new Cesium.EllipsoidSurfaceAppearance({
      material: getRingM({ color: "#00ff00" }), // 交替
      faceForward: true,
      closed: true,
    }),
  });

  viewer.scene.primitives.add(cone);
}
// 圆锥计算
function adjustAngle({ startAngle, endAngle }) {
  if (endAngle < startAngle) {
    if (endAngle > 180 && startAngle < 180) {
      var Az = startAngle; //先大小-前后交换顺序
      startAngle = endAngle;
      endAngle = Az;
    }
  }
  if (Math.abs(endAngle - startAngle) > 180) {
    if (endAngle > 180) {
      Az = startAngle;
      startAngle = endAngle - 360;
      endAngle = startAngle;
    }
  }
  return {
    startAngle,
    endAngle,
  };
}
function calcBeamMatrix4({ lon, lat, alt, pitch, heading, length }) {
  //北天东与cesium坐标系的差距
  heading += 90;
  pitch = 270 - pitch;
  var center = Cesium.Cartesian3.fromDegrees(lon, lat, alt);
  heading = Cesium.Math.toRadians(heading);
  pitch = Cesium.Math.toRadians(pitch);

  var deadingPitchRoll = new Cesium.HeadingPitchRoll(heading, pitch, 0);
  var hprRotation = Cesium.Matrix3.fromHeadingPitchRoll(deadingPitchRoll);

  var subtract = new Cesium.Cartesian3(0, 0, -length / 2);
  var rotated = Cesium.Matrix3.multiplyByVector(
    hprRotation,
    subtract,
    new Cesium.Cartesian3()
  );

  var hpr = Cesium.Matrix4.fromRotationTranslation(hprRotation, rotated);

  var modelMatrix = Cesium.Transforms.eastNorthUpToFixedFrame(center);
  Cesium.Matrix4.multiply(modelMatrix, hpr, modelMatrix);
  return modelMatrix;
}
function calcBeamScaleMatrix4({
  lon,
  lat,
  alt,
  length,
  startAz, //波束起始方位角
  endAz, //波束终止方位角
  startEl, //俯仰
  endEl, //俯仰
}) {
  //俯仰在正负180之间
  if (startEl > 180) startEl -= 360;
  if (startEl < -180) startEl += 360;
  if (endEl > 180) endEl -= 360;
  if (endEl < -180) endEl += 360;

  var newParam = adjustAngle({
    startAngle: startAz,
    endAngle: endAz,
  });
  startAz = newParam.startAngle;
  endAz = newParam.endAngle;

  var newParam1 = adjustAngle({
    startAngle: startEl,
    endAngle: endEl,
  });
  startEl = newParam1.startAngle;
  endEl = newParam1.endAngle;

  var heading = (endAz + startAz) / 2; //圆锥的方位指向
  var pitch = ((startEl + endEl) / 2) % 180; //圆锥的俯仰指向俯仰在-180到180之间

  var angle0 = (endAz - startAz) / 2; //方位
  var angle1 = (endEl - startEl) / 2; //俯仰

  if (endAz < 0 && startAz < 0) {
    //计算缩放比列,取最小角度和取绝对值
    angle0 = Math.abs(((endAz % 360) - (startAz % 360)) / 2); //方位圆锥底面
    angle1 = Math.abs(((endEl % 360) - (startEl % 360)) / 2); //俯仰圆锥底面
  }
  var angle = 0;
  var scaleX = 1,
    scaleY = 1;
  if (angle0 < angle1) {
    angle = angle1;
    scaleX = angle0 / angle1; //计算x,y,z的缩放比例(以便形成椭圆面)
  } else {
    angle = angle0;
    scaleY = angle1 / angle0; //计算x,y,z的缩放比例(以便形成椭圆面)
  }

  var bottomRadius = length * Math.tan(Cesium.Math.toRadians(angle)); //计算底边半径(以最小的方位或俯仰角为准)
  var modelMatrix = calcBeamMatrix4({
    lon,
    lat,
    alt,
    heading,
    pitch,
    length,
  });

  var s = Cesium.Matrix3.fromScale(new Cesium.Cartesian3(scaleY, scaleX, 1.0));
  var scaleMatrix = Cesium.Matrix4.fromRotationTranslation(s);

  let mm = modelMatrix.clone();
  Cesium.Matrix4.multiply(mm, scaleMatrix, mm);
  return { mm, bottomRadius };
}
// 圆锥材质
function getRingM(params) {
  var { color, thickness, repeat } = params || {};
  var material = new Cesium.Material({
    fabric: {
      type: "VtxfShader",
      uniforms: {
        color: Cesium.Color.fromCssColorString(color || "rgba(255,0,0,0.3)"),
        repeat: repeat || 40.0, //40.0,/重复次数
        offset: 10, // 1.0,
        thickness: thickness || 0.5, //环宽度比列(0, 1]
      },
      source: `
                      uniform vec4 color;
                      uniform float repeat;
                      uniform float offset;
                      uniform float thickness;
                      czm_material czm_getMaterial(czm_materialInput materialInput)
                      {
                          czm_material material = czm_getDefaultMaterial(materialInput);
                          float sp = 1.0/repeat;
                          vec2 st = materialInput.st;
                          float dis = distance(st, vec2(0.5));
                          float m = mod(dis + offset, sp);
                          float a = step(sp*(1.0-thickness), m);
                          material.diffuse = color.rgb;
                          material.alpha = a * color.a;
                          return material;
                      }
                  `,
    },
    // translucent: false,
  });
  return material;
}

// 初始化点位/线条
function initPointAndLine() {
  let temp = ref([]);

  for (let i = 0; i < pointArr.value.length; i++) {
    createPoint(pointArr.value[i]);

    temp.value.push(
      pointArr.value[i].lon,
      pointArr.value[i].lat,
      pointArr.value[i].alt
    );
  }

  createLine(temp.value);
}
// 初始化圆锥
function initCone() {
  let info = {
    lon: 123.649379, // 经度
    lat: 22.033591, // 纬度
    alt: 0, // 高度
    length: 200, // 圆锥高度
    angel: 45, // 方位
    pitch: 30, // 俯仰
  };

  createCone(info);
}
// 初始化侧向线
function initSideline() {
  viewer.entities.add({
    id: `Sideline`,
    polyline: {
      positions: new Cesium.CallbackProperty(() => {
        return aggregate.value;
      }, false),
      width: 1,
      material: new Cesium.StripeMaterialProperty({
        // 用条纹材质设置外边颜色
        evenColor: Cesium.Color.WHITE, // 偶数条纹颜色
        oddColor: Cesium.Color.WHITE, // 奇数条纹颜色
        repeat: 3.0, // 条纹的重复数量
      }),
    },
  });
}

// ------------------<<飞行测试>>------------------
const start = ref(0);
const stop = ref(0);
const flyTime = ref(0);

function addModels() {
  // 计算时间飞行数据
  let property = computeFlight(pointArr.value);

  // ++++++++++<<创建模型>>++++++++++
  const entity = viewer.entities.add({
    id: `testModel`, //模型id
    name: "model", // 模型名称,这里用作模型类型,方便场景模型增删改查
    // 和时间轴关联
    availability: new Cesium.TimeIntervalCollection([
      new Cesium.TimeInterval({
        start: start.value,
        stop: stop.value,
      }),
    ]),
    position: property, //模型位置,高度
    orientation: new Cesium.VelocityOrientationProperty(property),
    model: {
      uri: "./model/test1.gltf", //模型文件
      minimumPixelSize: 100, //模型最小像素大小
      maximumScale: 100, //模型最大像素大小
    },
  });
}
// 移动
function modelMove() {
  for (let i = 0; i < pointArr.value.length; i++) {
    pointArr.value[i].time = flyTime.value;

    flyTime.value += 2;
  }

  // 起始时间
  start.value = Cesium.JulianDate.fromDate(new Date());
  // 结束时间
  stop.value = Cesium.JulianDate.addSeconds(
    start.value,
    pointArr.value[pointArr.value.length - 1].time,
    new Cesium.JulianDate()
  );

  // 设置始时钟始时间
  viewer.clock.startTime = start.value.clone();
  // 设置时钟当前时间
  viewer.clock.currentTime = start.value.clone();
  // 设置始终停止时间
  viewer.clock.stopTime = stop.value.clone();
  // 时间速率,数字越大时间过的越快
  viewer.clock.multiplier = 1;
  // 时间轴
  // viewer.timeline.zoomTo(start.value, stop.value);
  // 循环执行,即为2,到达终止时间,重新从起点时间开始LOOP_STOP
  // viewer.clock.clockRange = Cesium.ClockRange.CLAMPED;
  viewer.clock.clockRange = Cesium.ClockRange.LOOP_STOP;

  // 创建模型
  addModels();
}
// 计算飞行
function computeFlight(source) {
  // 取样位置 相当于一个集合
  let property = new Cesium.SampledPositionProperty();
  for (let i = 0; i < source.length; i++) {
    let time = Cesium.JulianDate.addSeconds(
      start.value,
      source[i].time,
      new Cesium.JulianDate()
    );
    let position = Cesium.Cartesian3.fromDegrees(
      source[i].lon,
      source[i].lat,
      source[i].alt
    );
    // 添加位置,和时间对应
    property.addSample(time, position);
  }
  return property;
}
// 获取当前飞机位置
function getCurrentPosition() {
  if (!lineTimer.value) {
    // 定时获取实体位置
    lineTimer.value = setInterval(() => {
      // 获取飞机模型
      let plane = viewer.entities.getById("testModel");

      var planePosition = Cesium.Property.getValueOrDefault(
        plane._position,
        viewer.clock.currentTime,
        new Cesium.Cartesian3()
      );
      var cartographic = Cesium.Cartographic.fromCartesian(planePosition);
      var lon = Cesium.Math.toDegrees(cartographic.longitude);
      var lat = Cesium.Math.toDegrees(cartographic.latitude);

      aggregate.value = Cesium.Cartesian3.fromDegreesArrayHeights([
        123.649379,
        22.033591,
        0,
        lon,
        lat,
        300,
      ]);
    }, 10);
  }
}

// ---------------------------<<执行>>---------------------------
// 挂载后生命周期
onMounted(() => {
  initMap();
  initPointAndLine();
  initCone();
  initSideline();
  getCurrentPosition();

  // 测试飞行
  modelMove();
});
</script>
3、CSS
<style>
.cesium_map {
  width: 100%;
  height: 100%;
  position: relative;
  z-index: 1;
}

.cesium-widget-credits {
  display: none !important;
  visibility: hide !important;
}

.cesium_position {
  width: 240px;
  position: absolute;
  display: flex;
  height: 22px;
  line-height: 22px;
  background-color: rgba(0, 0, 0, 0.6);
  padding: 0 8px;

  right: 20px;
  bottom: 5px;
}

.cesium_position .position_lonlat {
  flex: 1;
  display: flex;
  color: #fff;
  font-size: 12px;
}

.cesium_position .position_lonlat p:first-child {
  flex: 0 0 40px;
}

.cesium_position .position_lonlat p:last-child {
  flex: 1;
}

#cesiumContainer {
  width: 100%;
  height: 100%;
  margin: 0;
  padding: 0;
  overflow: hidden;
}

.cesium-viewer-toolbar,
.navigation-controls,
.cesium-widget-credits,
.cesium-performanceDisplay-throttled {
  display: none !important;
}

.cesium-container {
  position: relative;
}

.distance-legend {
  right: 20px;
}

.distance-legend-label {
  font-size: 12px;
}

.gide_item_inpu {
  flex: 1;

  :deep(.el-switch__core) {
    border: 1px solid #2fedba;
  }

  :deep(.el-input.is-disabled .el-input__wrapper) {
    background-color: transparent;
    box-shadow: unset;
  }
}
</style>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值