Skip to content

公共函数

地图相关函数封装

代码出处: 来源

  • 提取地球中心点坐标:latlng.getCenter(viewer)
  • 提取地球视域边界:latlng.getExtent(viewer)
  • 提取视域边界:latlng.getViewBounds(viewer)
  • 提取相机视角范围参数:latlng.getCameraView(viewer)
  • 获取当前鼠标移动的位置:latlng.getCurrentMousePosition(viewer.scene, movement.endPosition, null),Cesium.ScreenSpaceEventType.MOUSE_MOVE 事件
点击查看代码
js
var latlng = {};
/**
 * 获取鼠标当前的屏幕坐标位置的三维Cesium坐标
 * @param {Cesium.Scene} scene
 * @param {Cesium.Cartesian2} position 二维屏幕坐标位置
 * @param {Cesium.Entity} noPickEntity 排除的对象(主要用于绘制中,排除对自己本身的拾取)
 */
latlng.getCurrentMousePosition = function (scene, position, noPickEntity) {
  var cartesian;

  //在模型上提取坐标
  var pickedObject = scene.pick(position);
  if (scene.pickPositionSupported && Cesium.defined(pickedObject)) {
    //pickPositionSupported :判断是否支持深度拾取,不支持时无法进行鼠标交互绘制
    var entity = pickedObject.id;
    if (noPickEntity == null || (noPickEntity && entity !== noPickEntity)) {
      var cartesian = scene.pickPosition(position);
      if (Cesium.defined(cartesian)) {
        var cartographic = Cesium.Cartographic.fromCartesian(cartesian);
        var height = cartographic.height; //模型高度
        if (height >= 0) return cartesian;

        //不是entity时,支持3dtiles地下
        if (!Cesium.defined(pickedObject.id) && height >= -500)
          return cartesian;
      }
    }
  }

  //测试scene.pickPosition和globe.pick的适用场景 https://zhuanlan.zhihu.com/p/44767866
  //1. globe.pick的结果相对稳定准确,不论地形深度检测开启与否,不论加载的是默认地形还是别的地形数据;
  //2. scene.pickPosition只有在开启地形深度检测,且不使用默认地形时是准确的。
  //注意点: 1. globe.pick只能求交地形; 2. scene.pickPosition不仅可以求交地形,还可以求交除地形以外其他所有写深度的物体。

  //提取鼠标点的地理坐标
  if (scene.mode === Cesium.SceneMode.SCENE3D) {
    //三维模式下
    var pickRay = scene.camera.getPickRay(position);
    cartesian = scene.globe.pick(pickRay, scene);
  } else {
    //二维模式下
    cartesian = scene.camera.pickEllipsoid(position, scene.globe.ellipsoid);
  }
  return cartesian;
};

/**
 * 提取地球中心点坐标
 * @param {Cesium.Viewer} viewer
 * */
latlng.getCenter = function (viewer) {
  var scene = viewer.scene;
  var target = pickCenterPoint(scene);
  var bestTarget = target;
  if (!bestTarget) {
    var globe = scene.globe;
    var carto = scene.camera.positionCartographic.clone();
    var height = globe.getHeight(carto);
    carto.height = height || 0;
    bestTarget = Cesium.Ellipsoid.WGS84.cartographicToCartesian(carto);
  }

  var result = latlng.formatPositon(bestTarget);

  // 获取地球中心点世界位置  与  摄像机的世界位置  之间的距离
  var distance = Cesium.Cartesian3.distance(
    bestTarget,
    viewer.scene.camera.positionWC
  );
  result.cameraZ = distance;

  return result;
};

function pickCenterPoint(scene) {
  var canvas = scene.canvas;
  var center = new Cesium.Cartesian2(
    canvas.clientWidth / 2,
    canvas.clientHeight / 2
  );

  var ray = scene.camera.getPickRay(center);
  var target = scene.globe.pick(ray, scene);
  return target || scene.camera.pickEllipsoid(center);
}

/**
 * 提取地球视域边界
 * @param {Cesium.Viewer} viewer
 * */
latlng.getExtent = function (viewer) {
  // 范围对象
  var extent = {
    xmin: 70,
    xmax: 140,
    ymin: 0,
    ymax: 55,
  }; //默认值:中国区域

  // 得到当前三维场景
  var scene = viewer.scene;

  // 得到当前三维场景的椭球体
  var ellipsoid = scene.globe.ellipsoid;
  var canvas = scene.canvas;

  // canvas左上角
  var car3_lt = viewer.camera.pickEllipsoid(
    new Cesium.Cartesian2(0, 0),
    ellipsoid
  );
  if (car3_lt) {
    // 在椭球体上
    var carto_lt = ellipsoid.cartesianToCartographic(car3_lt);
    extent.xmin = Cesium.Math.toDegrees(carto_lt.longitude);
    extent.ymax = Cesium.Math.toDegrees(carto_lt.latitude);
  } else {
    // 不在椭球体上
    var xMax = canvas.width / 2;
    var yMax = canvas.height / 2;

    var car3_lt2;
    // 这里每次10像素递加,一是10像素相差不大,二是为了提高程序运行效率
    for (var yIdx = 0; yIdx <= yMax; yIdx += 10) {
      var xIdx = yIdx <= xMax ? yIdx : xMax;
      car3_lt2 = viewer.camera.pickEllipsoid(
        new Cesium.Cartesian2(xIdx, yIdx),
        ellipsoid
      );
      if (car3_lt2) break;
    }
    if (car3_lt2) {
      var carto_lt = ellipsoid.cartesianToCartographic(car3_lt2);
      extent.xmin = Cesium.Math.toDegrees(carto_lt.longitude);
      extent.ymax = Cesium.Math.toDegrees(carto_lt.latitude);
    }
  }

  // canvas右下角
  var car3_rb = viewer.camera.pickEllipsoid(
    new Cesium.Cartesian2(canvas.width, canvas.height),
    ellipsoid
  );
  if (car3_rb) {
    // 在椭球体上
    var carto_rb = ellipsoid.cartesianToCartographic(car3_rb);
    extent.xmax = Cesium.Math.toDegrees(carto_rb.longitude);
    extent.ymin = Cesium.Math.toDegrees(carto_rb.latitude);
  } else {
    // 不在椭球体上
    var xMax = canvas.width / 2;
    var yMax = canvas.height / 2;

    var car3_rb2;
    // 这里每次10像素递减,一是10像素相差不大,二是为了提高程序运行效率
    for (var yIdx = canvas.height; yIdx >= yMax; yIdx -= 10) {
      var xIdx = yIdx >= xMax ? yIdx : xMax;
      car3_rb2 = viewer.camera.pickEllipsoid(
        new Cesium.Cartesian2(xIdx, yIdx),
        ellipsoid
      );
      if (car3_rb2) break;
    }
    if (car3_rb2) {
      var carto_rb = ellipsoid.cartesianToCartographic(car3_rb2);
      extent.xmax = Cesium.Math.toDegrees(carto_rb.longitude);
      extent.ymin = Cesium.Math.toDegrees(carto_rb.latitude);
    }
  }

  //交换
  if (extent.xmax < extent.xmin) {
    var temp = extent.xmax;
    extent.xmax = extent.xmin;
    extent.xmin = temp;
  }
  if (extent.ymax < extent.ymin) {
    var temp = extent.ymax;
    extent.ymax = extent.ymin;
    extent.ymin = temp;
  }

  return extent;
};
/**
 * 提取视域边界
 * @param {Cesium.Viewer} viewer
 *
 * */
latlng.getViewBounds = function (viewer) {
  var rectangle = viewer.camera.computeViewRectangle();
  // 弧度转为经纬度,west为左(西)侧边界的经度,以下类推
  var west = (rectangle.west / Math.PI) * 180;
  var north = (rectangle.north / Math.PI) * 180;
  var east = (rectangle.east / Math.PI) * 180;
  var south = (rectangle.south / Math.PI) * 180;
  // 鉴于高德、leaflet获取的边界都是southwest和northeast字段来表示,本例保持一致性
  var bounds = {
    southwest: {
      lng: west,
      lat: south,
    },
    northeast: {
      lng: east,
      lat: north,
    },
  };
  return bounds;
};

/**
 * 提取相机视角范围参数
 * @param {Cesium.Viewer} viewer
 *
 * */
latlng.getCameraView = function (viewer) {
  var camera = viewer.camera;
  var position = camera.positionCartographic;

  var bookmark = {};
  bookmark.y = Number(Cesium.Math.toDegrees(position.latitude).toFixed(6));
  bookmark.x = Number(Cesium.Math.toDegrees(position.longitude).toFixed(6));
  bookmark.z = Number(position.height.toFixed(1));
  bookmark.heading = Number(
    Cesium.Math.toDegrees(camera.heading || -90).toFixed(1)
  );
  bookmark.pitch = Number(Cesium.Math.toDegrees(camera.pitch || 0).toFixed(1));
  bookmark.roll = Number(Cesium.Math.toDegrees(camera.roll || 0).toFixed(1));

  return bookmark;
};

/**
 * 格式化坐标点为可显示的可理解格式(如:经度x:123.345345、纬度y:31.324324、高度z:123.1)。
 * @param {Cesium.Cartesian3} position
 * */
latlng.formatPositon = function (position) {
  var carto = Cesium.Cartographic.fromCartesian(position);
  var result = {};
  result.y = Number(Cesium.Math.toDegrees(carto.latitude).toFixed(6));
  result.x = Number(Cesium.Math.toDegrees(carto.longitude).toFixed(6));
  result.z = Number(carto.height.toFixed(1));
  return result;
};

//笛卡尔转经纬度/弧度
latlng.Cartesian3To = function (cartesian3, viewer) {
  if (cartesian3) {
    if (viewer) {
      var ellipsoid = viewer.scene.globe.ellipsoid;
      var cartographic;
      //转弧度
      cartographic = ellipsoid.cartesianToCartographic(cartesian3);
    } else {
      cartographic = Cesium.Cartographic.fromCartesian(cartesian3);
    }
    //转经纬度
    var lat = Cesium.Math.toDegrees(cartographic.latitude);
    var lng = Cesium.Math.toDegrees(cartographic.longitude);
    var height = cartographic.height;
    return {
      cartesian3: cartesian3,
      cartographic: cartographic,
      latlng: {
        lat: lat,
        lng: lng,
        height: height,
      },
    };
  }
};
//绕点 环绕飞行
latlng.windingPoint = {
  isStart: false,
  viewer: null,
  start: function (viewer, point) {
    if (!point) point = getCenter(viewer);

    this.viewer = viewer;
    this.position = Cesium.Cartesian3.fromDegrees(point.x, point.y, point.z);

    this.distance =
      point.distance ||
      Cesium.Cartesian3.distance(this.position, viewer.camera.positionWC); // 给定相机距离点多少距离飞行
    this.angle = 360 / (point.time || 60); //time:给定飞行一周所需时间(单位 秒)

    this.time = viewer.clock.currentTime.clone();
    this.heading = viewer.camera.heading; // 相机的当前heading
    this.pitch = viewer.camera.pitch;

    this.viewer.clock.onTick.addEventListener(this.clock_onTickHandler, this);
    this.isStart = true;
  },
  clock_onTickHandler: function (e) {
    var delTime = Cesium.JulianDate.secondsDifference(
      this.viewer.clock.currentTime,
      this.time
    ); // 当前已经过去的时间,单位 秒
    var heading = Cesium.Math.toRadians(delTime * this.angle) + this.heading;

    this.viewer.scene.camera.setView({
      destination: this.position, // 点的坐标
      orientation: {
        heading: heading,
        pitch: this.pitch,
      },
    });
    this.viewer.scene.camera.moveBackward(this.distance);
  },
  stop: function () {
    if (!this.isStart) return;

    if (this.viewer)
      this.viewer.clock.onTick.removeEventListener(
        this.clock_onTickHandler,
        this
      );
    this.isStart = false;
  },
};

//固定点 向四周旋转
latlng.aroundPoint = {
  isStart: false,
  viewer: null,
  start: function (viewer, point) {
    if (!point) point = getCenter(viewer);

    this.viewer = viewer;
    this.position = Cesium.Cartesian3.fromDegrees(point.x, point.y, point.z);

    this.angle = 360 / (point.time || 60); //time:给定飞行一周所需时间(单位 秒)

    this.time = viewer.clock.currentTime.clone();
    this.heading = viewer.camera.heading; // 相机的当前heading
    this.pitch = viewer.camera.pitch;

    this.viewer.clock.onTick.addEventListener(this.clock_onTickHandler, this);
    this.isStart = true;
  },
  clock_onTickHandler: function (e) {
    // 当前已经过去的时间,单位s
    var delTime = Cesium.JulianDate.secondsDifference(
      this.viewer.clock.currentTime,
      this.time
    );
    var heading = Cesium.Math.toRadians(delTime * this.angle) + this.heading;
    viewer.scene.camera.setView({
      orientation: {
        heading: heading,
        pitch: this.pitch,
      },
    });
  },
  stop: function () {
    if (!this.isStart) return;

    if (this.viewer)
      this.viewer.clock.onTick.removeEventListener(
        this.clock_onTickHandler,
        this
      );
    this.isStart = false;
  },
};