持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第7天,点击查看活动详情
“金秋十月,我要连续30天更文,做劳模,拿手机摄影神器!点击查看活动详情 “即可成功参与
领导又来了新需求,太难受了/(ㄒoㄒ)/~~,这次的需求是实现 cesium-二三维联,话不多说直接开始吧!
cesium-ol二三维联动(鹰眼图)
cesium除了使用两个viewer联动的方式实现鹰眼图,还有其他方式
这里我使用cesium和ol联动的方式实现鹰眼图(也可以实现简单的二三维联动效果)
实现思路
1、监听cesium地图的移动
2、当移动视图时,计算cesium视图范围,并传递给ol
3、监听ol地图移动,当ol移动时,计算ol的视图范围
4、传递给cesium(cesium会垂直移动)
html
<template>
<div class="main-view" style="position: relative; display: flex; height: 800px">
<div class="toolbox">
<button id="point" style="color: white; margin-bottom: 20px" class="tool-item">联动</button>
<button id="clear" style="color: white" class="tool-item">关闭</button>
</div>
<div id="cesiumContainer"></div>
</div>
</template>
#cesiumContainer {
width: 100%;
height: 100%;
margin: 0;
padding: 0;
overflow: hidden;
display: flex;
}
.toolbox {
position: absolute;
top: 49%;
left: 30px;
z-index: 99;
display: flex;
flex-direction: column;
justify-content: space-around;
}
.tool-item {
background-color: #303336;
cursor: pointer;
}
#eye {
position: absolute;
width: 581px !important;
height: 581px !important;
bottom: 0;
right: 0;
z-index: 999;
border: solid blue 1px;
}
核心js代码
<script setup>
import * as Cesium from 'cesium';
import Cesium2DLinkage3DUtil from './map'; //引入核心js代码
import { onMounted } from 'vue';
//加载3D地图
const init3d = () => {
const viewer = new Cesium.Viewer('cesiumContainer', {
geocoder: false,
homeButton: false,
sceneModePicker: false,
baseLayerPicker: false,
navigationHelpButton: false,
animation: false,
timeline: false,
fullscreenButton: false,
vrButton: false,
mapProjection: new Cesium.WebMercatorProjection()
// sceneMode: Cesium.SceneMode.SCENE2D
});
viewer._cesiumWidget._creditContainer.style.display = 'none'; // 隐藏logo版权
// 天地图加载
const td = '02ed99e199228a5d47960a0324894605'; // 一天只能请求一万次啊
const TDTImgProvider = new Cesium.WebMapTileServiceImageryProvider({
url:
'http://t{s}.tianditu.com/img_w/wmts?service=wmts&request=GetTile&version=1.0.0&LAYER=img&tileMatrixSet=w&TileMatrix={TileMatrix}&TileRow={TileRow}&TileCol={TileCol}&style=default&format=tiles&tk=' +
td,
layer: '天地图影像',
style: 'default',
format: 'image/jpeg',
subdomains: ['0', '1', '2', '3', '4', '5', '6', '7'],
tileMatrixSetID: 'GoogleMapsCompatible'
});
const TDTZJProvider = new Cesium.WebMapTileServiceImageryProvider({
url:
'http://t{s}.tianditu.com/cia_w/wmts?service=wmts&request=GetTile&version=1.0.0&LAYER=cia&tileMatrixSet=w&TileMatrix={TileMatrix}&TileRow={TileRow}&TileCol={TileCol}&style=default.jpg&tk=' +
td,
layer: '天地图中文注记',
style: 'default',
format: 'image/jpeg',
subdomains: ['0', '1', '2', '3', '4', '5', '6', '7'],
tileMatrixSetID: 'GoogleMapsCompatible'
});
// 地形添加
viewer.terrainProvider = new Cesium.CesiumTerrainProvider({
url: 'https://nanchang.3zyun.com/terrain/t'
});
viewer.imageryLayers.addImageryProvider(TDTImgProvider);
viewer.imageryLayers.addImageryProvider(TDTZJProvider);
// 相机聚焦 将三维球定位到中国 相机飞行
viewer.camera.flyTo({
destination: Cesium.Cartesian3.fromDegrees(103.84, 31.15, 17850000), // 经纬度 高度
orientation: {
heading: Cesium.Math.toRadians(348.4202942851978),
pitch: Cesium.Math.toRadians(-89.74026687972041),
roll: Cesium.Math.toRadians(0)
},
complete: function callback() {
// 定位完成之后的回调函数
console.log(' 定位完成之后的回调函数');
}
});
/* // 设置相机的位置和方向
viewer.camera.setView({
// fromDegrees()方法,将经纬度和高程转换为世界坐标
destination: Cesium.Cartesian3.fromDegrees(103.84, 31.15, 17850000),
orientation: {
// 指向
heading: Cesium.Math.toRadians(90, 0),
// 视角
pitch: Cesium.Math.toRadians(-90),
roll: 0.0
}
}); */
viewer._cesiumWidget._creditContainer.style.display = 'none'; // 隐藏版权
//具体实现联动方法(核心方法)
const linkUtil = new Cesium2DLinkage3DUtil();
document.getElementById('point').onclick = function () {
if (linkUtil.isActive) return;
linkUtil.active(viewer);
};
document.getElementById('clear').onclick = function () {
linkUtil.deactive();
};
};
onMounted(() => {
init3d(); // 加载
});
</script>
核心js:
/**
* 二三维联动,二维openlayers,三维Cesium
*/
import * as Cesium from 'cesium';
import 'ol/ol.css';
import TileGrid from 'ol/tilegrid/TileGrid';
import * as ol from 'ol';
import { Tile as TileLayer } from 'ol/layer';
import { defaults } from 'ol/control';
import XYZ from 'ol/source/XYZ';
import { transformExtent, transform } from 'ol/proj';
export default class Cesium2DLinkage3DUtil {
constructor(mapId = 'eye') {
this.mapId = mapId;
this.isActive = false;
this.isIn2DMapFlag = false;
this.mouseMoveEvent = this.mouseMoveEvent.bind(this);
this.getViewCameraRectrange = this.getViewCameraRectrange.bind(this);
this.changeCenterListener = this.changeCenterListener.bind(this);
}
/**
* 初始化ol地图容器,插入三维容器的左侧
*/
init2DDiv() {
this.mapDiv = document.createElement('div');
this.mapDiv.setAttribute('id', this.mapId);
this.mapDiv.style.width = 1500 + 'px';
// insertBefore
const viewerContainer = this.viewer.cesiumWidget.container.parentElement.parentElement;
viewerContainer.parentNode.insertBefore(this.mapDiv, viewerContainer);
}
/**
* 初始化ol地图视图
*/
init2DMap() {
const resolutions = [];
const tilegrid = new TileGrid({
origin: [0, 0], // 设置原点坐标
resolutions // 设置分辨率
});
// 普通地图
const tiandituVecLayer = new TileLayer({
projection: 'EPSG:3857',
opacity: 1,
tileGrid: tilegrid,
source: new XYZ({
url: 'https://t3.tianditu.gov.cn/DataServer?T=img_w&x={x}&y={y}&l={z}&tk=82ddfa50085e53ebbeb11b6c91988f6a'
})
});
// 普通地图标记
const tiandituCvaLayer = new TileLayer({
projection: 'EPSG:3857',
opacity: 1,
tileGrid: tilegrid,
source: new XYZ({
url: 'https://t3.tianditu.gov.cn/DataServer?T=cia_w&x={x}&y={y}&l={z}&tk=82ddfa50085e53ebbeb11b6c91988f6a'
})
});
this.olMap = new ol.Map({
layers: [tiandituVecLayer, tiandituCvaLayer],
target: this.mapId,
view: new ol.View({
center: [103.84, 31.15], // 地图中心点
zoom: 8,
projection: 'EPSG:3857',
maxZoom: 22
}),
// 设置地图控件,默认的三个控件都不显示
controls: defaults({
attribution: false,
rotate: false,
zoom: false
})
});
this.olMap.updateSize();
}
/**
* 二维监听事件处理
*/
changeCenterListener() {
if (this.isIn2DMapFlag) {
const bounds = this.olMap.getView().calculateExtent();
const boundsTansform = transformExtent(bounds, 'EPSG:3857', 'EPSG:4326');
this.viewer.camera.setView({
heading: Cesium.Math.toRadians(120.0), // 方向
pitch: Cesium.Math.toRadians(-10), // 倾斜角度
roll: this.viewer.camera.roll,
destination: Cesium.Rectangle.fromDegrees(boundsTansform[0], boundsTansform[1], boundsTansform[2], boundsTansform[3])
});
}
}
/**
* 三维监听事件处理
*/
getViewCameraRectrange() {
const rectangle = this.viewer.camera.computeViewRectangle();
// 弧度转为经纬度
const west = (rectangle.west / Math.PI) * 180;
const north = (rectangle.north / Math.PI) * 180;
const east = (rectangle.east / Math.PI) * 180;
const south = (rectangle.south / Math.PI) * 180;
// 三维联动二维界面
if (!this.isIn2DMapFlag) {
if (north > 87 && south < -87) {
const center = this.getCenterPosition(this.viewer);
this.olMap.getView().setZoom(0);
this.olMap.getView().setCenter(transform([center.lon, center.lat], 'EPSG:4326', 'EPSG:3857'));
} else {
this.olMap.getView().fit(transformExtent([west, south, east, north], 'EPSG:4326', 'EPSG:3857'));
}
}
}
/**
* 判断鼠标是否在二维地图
* @param x
* @param y
* @return {boolean}
*/
isMouseIn2DMap(x, y) {
const y1 = this.mapDiv.offsetTop; // div上面两个的点的y值
const y2 = y1 + this.mapDiv.clientHeight; // div下面两个点的y值
const x1 = this.mapDiv.offsetLeft; // div左边两个的点的x值
const x2 = x1 + this.mapDiv.clientWidth; // div右边两个点的x的值
return !(x < x1 || x > x2 || y < y1 || y > y2);
}
addListener() {
this.olMap.getView().on('change:center', this.changeCenterListener);
this.olMap.updateSize();
this.viewer.cesiumWidget.container.parentElement.parentElement.parentElement.addEventListener('mousemove', this.mouseMoveEvent);
this.viewer.scene.preRender.addEventListener(this.getViewCameraRectrange);
}
removeListener() {
this.olMap.getView().un('change:center', this.changeCenterListener);
this.viewer.scene.preRender.removeEventListener(this.getViewCameraRectrange);
this.viewer.cesiumWidget.container.parentElement.parentElement.parentElement.removeEventListener('mousemove', this.mouseMoveEvent);
this.mapDiv.style.width = '0%';
this.mapDiv.parentNode.removeChild(this.mapDiv);
this.olMap = null;
}
mouseMoveEvent(e) {
this.isIn2DMapFlag = this.isMouseIn2DMap(e.pageX, e.pageY);
}
getCenterPosition(viewer) {
const result = viewer.camera.pickEllipsoid(new Cesium.Cartesian2(viewer.canvas.clientWidth / 2, viewer.canvas.clientHeight / 2));
const curPosition = Cesium.Ellipsoid.WGS84.cartesianToCartographic(result);
const lon = (curPosition.longitude * 180) / Math.PI;
const lat = (curPosition.latitude * 180) / Math.PI;
return {
lon,
lat
};
}
active(viewer) {
this.viewer = viewer;
this.isActive = true;
this.init2DDiv();
this.init2DMap();
this.addListener();
}
deactive() {
this.removeListener();
this.isActive = false;
this.viewer = undefined;
}
}
实现效果:
最后实现了cesium-ol二三维联动(鹰眼图),点击关闭退出联动状态。
但还是有点问题:cesium和ol平面视角联动还算正常的,但是当cesium为三维视角时联动的效果就出现问题。下次解决