openscence.ts
/***
* 加载3维服务
* @param
*/
export default class OpenScene{
declare viewer: any
declare url: String
declare promise: any
declare handler: any
declare eventType: any
declare prompt: any
declare addClick: any
constructor(option: {
viewer: any,
url: String,
addClick: any
}) {
this.viewer = option.viewer
this.url = option.url
this.promise = undefined
this.handler = undefined
this.eventType = undefined
this.prompt = undefined
this.addClick = option.addClick
this.add()
}
/***
* 添加场景
* @private
*/
private add() {
this.promise = this.viewer.scene.open(this.url);
}
/***
* 添加场景完成
* @param {Function} callBack 加载完成回调
*/
public success(callBack){
Cesium.when(this.promise, (layer) => {
callBack(layer)
})
}
/***
* 获取所有图层
* @param {Function} callBack 获取所有图层回调
*/
public getAllLayersName(callback){
let arr = []
if(this.viewer.scene.layers?._layers?._array){
this.viewer.scene.layers._layers._array.forEach(item=>{
arr.push(item._name)
})
}
callback(arr)
}
/***
* 根据name移除图层
* @param { string } name 图层名称
*/
public removeLayer(name: string){
if(this.addClick){
this.addClick.removePopUp()
}
if(name){
this.viewer.scene.layers.remove(name)
}
}
/***
* 添加点击事件
* @param { function } callback 点击后的回调
* @public
*/
public on(callback){
if(this.addClick){
this.addClick.on((point,entity,data)=>{
callback(point,entity,data)
})
}
}
}
prompt.css
.easy3d-prompt {
position: absolute;
top: -9999px;
left: -9999px;
}
.prompt-close {
position: absolute;
top: 0;
right: 0;
padding: 4px 4px 0 0;
border: none;
text-align: center;
width: 18px;
height: 14px;
font: 16px/14px Tahoma, Verdana, sans-serif;
color: #c3c3c3;
text-decoration: none;
font-weight: bold;
background: transparent;
}
.prompt-content-container {
/* 不给padding撑不开div */
max-width: 400px;
border-radius: 4px;
padding: 1px;
background: white;
color: #333;
box-shadow: 0 3px 14px rgb(0 0 0 / 40%);
}
.prompt-content {
margin: 13px 19px;
}
.prompt-anchor-container {
position: absolute;
width: 40px;
height: 20px;
left: 50%;
margin-left: -20px;
overflow: hidden ;
pointer-events: none;
}
.prompt-anchor {
margin: -10px auto 0;
background: white;
width: 17px;
height: 17px;
-webkit-transform: rotate(45deg);
transform: rotate(45deg);
}
Prompt.ts
/**
* 气泡窗类
* @class
*
*/
import "./prompt.css"
import {AllowedComponentProps, ComponentCustomProps, ComponentOptionsMixin, ComputedOptions,
createVNode,
DefineComponent,
ExtractPropTypes,
MethodOptions,
render,
VNodeProps
} from "vue";
class Prompt {
/**
* @param {Cesium.Viewer} viewer 地图viewer对象
* @param {Object} opt
* @param {Cesium.Cartesian3 | Array} [opt.position] 弹窗坐标 (type=2时生效)
* @param {Boolean} opt.show 是否显示
* @param {Function} [opt.success] 创建成功的回调函数
* @param {Number} [opt.type=1] 1~位置变化提示框 / 2~固定坐标提示框
* @param {Cesium.Cartesian3 | Array} opt.position 固定坐标提示框的坐标( cartesian3 / [101,30] ),type为1时,可不设置此参数
* @param {Boolean} [opt.anchor=true] 是否显示锚点
* @param {Boolean} [opt.closeBtn=true] 是否显示关闭按钮
* @param {String} opt.className 自定义class
* @param {String} opt.content 弹窗内容
* @param {Function} [opt.close] 关闭弹窗时的回调函数
* @param {Object} [opt.offset] 偏移参数
* @param {Number} [opt.offset.x] 横坐标偏移像素单位
* @param {Number} [opt.offset.y] 纵坐标偏移像素单位
* @param {Object} [opt.style] 弹窗面板样式
* @param {String} [opt.style.background='white'] 背景色
* @param {String} [opt.style.boxShadow] 弹窗阴影(css属性)
* @param {String} [opt.style.color] 弹窗颜色
*
*/
constructor(viewer: any, opt: {
type?: any,
content?: any,
close?: Function,
position?: any
}) {
// @ts-ignore
this.viewer = viewer;
// @ts-ignore
if (!this.viewer) return;
// @ts-ignore
this.type = "prompt";
// 默认值
opt = opt || {};
const promptType = opt.type == undefined ? 1 : opt.type;
let defaultOpt = {
id: (new Date().getTime() + "" + Math.floor(Math.random() * 10000)),
type: promptType,
anchor: promptType == 2 ? true : false,
closeBtn: promptType == 2 ? true : false,
offset: promptType == 2 ? { x: 0, y: -20 } : { x: 10, y: 10 },
content: "",
props: "",
show: true,
style: {
background: "rgba(0,0,0,0.5)",
color: "white"
}
}
// @ts-ignore
this.opt = Object.assign(defaultOpt, opt);
/**
* @property {Object} attr 相关属性
*/
// @ts-ignore
this.attr = this.opt;
// ====================== 创建弹窗内容 start ======================
// @ts-ignore
const mapid = this.viewer.container.id;
/**
* @property {Boolearn} isShow 当前显示状态
*/
// @ts-ignore
this.isShow = this.opt.show == undefined ? true : this.opt.show; // 是否显示
let anchorHtml = ``;
let closeHtml = ``;
// @ts-ignore
const background = this.opt.style.background;
// @ts-ignore
const color = this.opt.style.color;
// @ts-ignore
if (this.opt.anchor) {
anchorHtml += `
${background} !important;">
`;
}
// @ts-ignore
if (this.opt.closeBtn) { // 移动提示框 不显示关闭按钮
// @ts-ignore
closeHtml = `${this.opt.id}" id="prompt-close-${this.opt.id}">x`;
}
// @ts-ignore
let boxShadow = this.opt.style.boxShadow;
// @ts-ignore
const promptId = "prompt-" + this.opt.id;
// @ts-ignore
const promptConenet = `${background} !important;color:${color} !important;box-shadow:${boxShadow} !important">${this.opt.id}">
${anchorHtml}
${closeHtml}
`;
// 构建弹窗元素
// @ts-ignore
this.promptDiv = window.document.createElement("div");
// @ts-ignore
this.promptDiv.className = `easy3d-prompt ${this.opt.className}`;
// @ts-ignore
this.promptDiv.id = promptId;
// @ts-ignore
this.promptDiv.innerHTML = promptConenet;
let mapDom = window.document.getElementById(mapid);
// @ts-ignore
mapDom.appendChild(this.promptDiv);
// @ts-ignore
const app = createVNode(this.opt.content, {data: this.opt.props})
// @ts-ignore
render(app, document.querySelector(`#prompt-content-${this.opt.id}`))
// @ts-ignore
const clsBtn = window.document.getElementById(`prompt-close-${this.opt.id}`);
let that = this;
if (clsBtn) {
clsBtn.addEventListener("click", (e) => {
that.hide();
// @ts-ignore
if (that.opt.close) that.opt.close();
})
}
/**
* @property {Object} promptDom 弹窗div
*/
// @ts-ignore
this.promptDom = window.document.getElementById(promptId);
// @ts-ignore
this.position = this.transPosition(this.opt.position);
// ====================== 创建弹窗内容 end ======================
if (promptType == 2) this.bindRender(); // 固定位置弹窗 绑定实时渲染 当到地球背面时 隐藏
// @ts-ignore
if (this.opt.show == false) this.hide();
// @ts-ignore
this.containerW = this.viewer.container.offsetWidth;
// @ts-ignore
this.containerH = this.viewer.container.offsetHeight;
// @ts-ignore
this.containerLeft = this.viewer.container.offsetLeft;
// @ts-ignore
this.containerTop = this.viewer.container.offsetTop;
/**
* @property {Number} contentW 弹窗宽度
*/
// @ts-ignore
this.contentW = Math.ceil(Number(this.promptDom.offsetWidth)); // 宽度
/**
* @property {Number} contentH 弹窗高度
*/
// @ts-ignore
this.contentH = this.promptDom.offsetHeight; // 高度
// @ts-ignore
if (this.opt.success) this.opt.success();
}
/**
* 销毁
*/
destroy() {
// @ts-ignore
if (this.promptDiv) {
// @ts-ignore
window.document.getElementById(this.viewer.container.id).removeChild(this.promptDiv);
// @ts-ignore
this.promptDiv = null;
}
// @ts-ignore
if (this.rendHandler) {
// @ts-ignore
this.rendHandler();
// @ts-ignore
this.rendHandler = null;
}
}
// 实时监听
bindRender() {
let that = this;
// @ts-ignore
this.rendHandler = this.viewer.scene.postRender.addEventListener(function () {
// @ts-ignore
if (!that.isShow && that.promptDom) {
// @ts-ignore
that.promptDom.style.display = "none";
return;
}
// @ts-ignore
if (!that.position) return;
// @ts-ignore
if (that.position instanceof Cesium.Cartesian3) {
// @ts-ignore
let px = Cesium.SceneTransforms.wgs84ToWindowCoordinates(that.viewer.scene, that.position);
if (!px) return;
// @ts-ignore
const occluder = new Cesium.EllipsoidalOccluder(that.viewer.scene.globe.ellipsoid, that.viewer.scene.camera.position);
// 当前点位是否可见 是否在地球背面
// @ts-ignore
const res = occluder.isPointVisible(that.position);
if (res) {
// @ts-ignore
if (that.promptDom) that.promptDom.style.display = "block";
} else {
// @ts-ignore
if (that.promptDom) that.promptDom.style.display = "none";
}
that.setByPX({
x: px.x,
y: px.y
});
} else {
that.setByPX({
// @ts-ignore
x: that.position.x,
// @ts-ignore
y: that.position.y
});
}
}, this);
}
/**
*
* @param {Cesium.Cartesian3 | Object} px 弹窗坐标
* @param {String} html 弹窗内容
*/
update(px: Cesium.Cartesian3 | Cesium.Cartesian2, html: any) {
if (px instanceof Cesium.Cartesian3) {
// @ts-ignore
this.position = px.clone();
// @ts-ignore
px = Cesium.SceneTransforms.wgs84ToWindowCoordinates(this.viewer.scene, px);
}
// @ts-ignore
this.contentW = Math.ceil(Number(this.promptDom.offsetWidth)); // 宽度
// @ts-ignore
this.contentH = this.promptDom.offsetHeight; // 高度
if (px) this.setByPX(px);
if (html) this.setContent(html);
}
// 判断是否在当前视野内
isInView() {
// @ts-ignore
if (!this.position) return false;
let px = null;
// @ts-ignore
if (this.position instanceof Cesium.Cartesian2) {
// @ts-ignore
px = this.position;
} else {
// @ts-ignore
px = Cesium.SceneTransforms.wgs84ToWindowCoordinates(this.viewer.scene, this.position);
}
// @ts-ignore
const occluder = new Cesium.EllipsoidalOccluder(this.viewer.scene.globe.ellipsoid, this.viewer.scene.camera.position);
// 是否在地球背面
// @ts-ignore
const res = occluder.isPointVisible(this.position);
let isin = false;
if (!px) return isin;
if (
// @ts-ignore
px.x > this.containerLeft &&
// @ts-ignore
px.x < (this.containerLeft + this.containerW) &&
// @ts-ignore
px.y > this.containerTop &&
// @ts-ignore
px.y < (this.containerTop + this.containerH)
) {
isin = true;
}
return res && isin;
}
/**
* 是否可见
* @param {Boolean} isShow true可见,false不可见
*/
setVisible(isShow: boolean) {
// @ts-ignore
let isin = this.isInView(this.position);
if (isin && isShow) {
// @ts-ignore
this.isShow = true;
// @ts-ignore
if (this.promptDom) this.promptDom.style.display = "block";
} else {
// @ts-ignore
this.isShow = false;
// @ts-ignore
if (this.promptDom) this.promptDom.style.display = "none";
}
}
/**
* 显示
*/
show() {
this.setVisible(true);
}
/**
* 隐藏
*/
hide() {
this.setVisible(false);
}
/**
* 设置弹窗内容
* @param {String} content 内容
*/
// @ts-ignore
setContent(content) {
// @ts-ignore
let pc = window.document.getElementById(`prompt-content-${this.opt.id}`);
// @ts-ignore
pc.innerHTML = content;
// @ts-ignore
const app = createVNode(content, {data: this.opt.props})
// @ts-ignore
render(app, document.querySelector(`#prompt-content-${this.opt.id}`))
}
/**
* 设置弹窗坐标
* @param {Object} opt 屏幕坐标
*/
// @ts-ignore
setByPX(opt) {
if (!opt) return;
// @ts-ignore
if (this.promptDom) {
// @ts-ignore
const contentW = this.promptDom.offsetWidth; // 宽度
// @ts-ignore
const contentH = this.promptDom.offsetHeight; // 高度
// @ts-ignore
if (this.opt.type == 1) {
// @ts-ignore
this.promptDom.style.left = ((Number(opt.x) + Number(this.opt.offset.x || 0))) + "px";
// @ts-ignore
this.promptDom.style.top = ((Number(opt.y) + Number(this.opt.offset.y || 0))) + "px";
} else {
// @ts-ignore
this.promptDom.style.left = ((Number(opt.x) + Number(this.opt.offset.x || 0)) - Number(this.contentW) / 2) + "px";
// @ts-ignore
this.promptDom.style.top = ((Number(opt.y) + Number(this.opt.offset.y || 0)) - Number(this.contentH)) + "px";
}
}
}
// 坐标转换
// @ts-ignore
transPosition(p) {
let position;
if (Array.isArray(p)) {
const posi = Cesium.Cartesian3.fromDegrees(p[0], p[1], p[2] || 0);
position = posi.clone();
} else if (p instanceof Cesium.Cartesian3) {
position = p.clone();
} else { // 像素类型
position = p;
}
return position;
}
}
export default Prompt;
HandleClick.ts
import Prompt from "@/components/SuperMap-iClient3D/CesiumPopup";
export default class HandleClick{
declare isClick: boolean
declare eventType: any
declare viewer: any
declare prompt: any
declare handler: any
declare popup: object
constructor(option: {
viewer: any,
popup: object,
eventType: string,
}) {
this.isClick = false
this.eventType = option.eventType
this.viewer = option.viewer
this.prompt = undefined
this.handler = undefined
this.popup = option.popup
}
/***
* 添加点击事件
* @param { function } callback 点击后的回调
* @public
*/
public on(callback){
if(this.isClick){
return
}
this.isClick = true
this.off()
let that = this;
this.handler = new Cesium.ScreenSpaceEventHandler(that.viewer.scene.canvas);
this.handler.setInputAction(function (click) {
that.removePopUp()
let pickedObject = that.viewer.scene.pick(click.position);
// 屏幕坐标转世界坐标——关键点
let ellipsoid = that.viewer.scene.globe.ellipsoid;
let cartesian = that.viewer.camera.pickEllipsoid(click.position, ellipsoid);
that.viewer._selectedEntity = [];//去除左击之后出现选中的绿框
if (cartesian) { //判断点击的是否是地球
//将笛卡尔坐标转换为地理坐标
const cartographic = Cesium.Cartographic.fromCartesian(cartesian);
//将弧度转为度的十进制度表示
const lon = Cesium.Math.toDegrees(cartographic.longitude);
const lat = Cesium.Math.toDegrees(cartographic.latitude);
const click_point = {longitude: lon, latitude: lat};
// that.viewer._element.style.cursor = 'pointer'
if (Cesium.defined(pickedObject)) {
if(that.viewer.scene.layers?._layers?._array){
that.viewer.scene.layers._layers._array.forEach(item=>{
let layer = that.viewer.scene.layers.find(item._name);
layer.indexedDBSetting.isAttributesSave = true
let ids = layer.getSelection()
ids.forEach(id=>{
layer.getAttributesById(id).then(function (data) {
if(data){
if(that.popup.component){
that.prompt = new Prompt(that.viewer,{
type: 2,
content: that.popup.component,
props: data,
position: [click_point.longitude, click_point.latitude, 0], // 支持多种形式传参 cartesian3 || array || object
close: function () {
return false
} // 点击关闭按钮的回调函数
});
}
}
callback(click_point,pickedObject,data)
})
})
})
}
}
}
}, Cesium.ScreenSpaceEventType[this.eventType])
}
/***
* 清除点击事件
* @public
*/
public off(){
if(this.handler && this.eventType){
this.handler.removeInputAction(Cesium.ScreenSpaceEventType[this.eventType]);
this.handler.destroy()
}
}
/***
* 清除弹框
* @public
*/
public removePopUp(){
if(this.prompt){
this.prompt.destroy()
}
}
}
只注册一个点击事件
handleClick = new HandleClick({
viewer: viewer,
popup: {
component: popup
},
eventType: 'LEFT_CLICK'
})
scence1 = new OpenScene({
viewer: getMap(),
url: 'http://localhost:8090/iserver/services/3D-ReComputeNormalResult-test/rest/realspace',
addClick: handleClick
})
scence1.success(()=>{
scence1.getAllLayersName((nameArr)=>{
nameArr.forEach(name=>{
let line = getMap().scene.layers.find(name);
line.textureUVSpeed = new Cesium.Cartesian2(0, -1); //模型纹理在UV坐标上的运动速度
})
})
scence1.on((point, layerId, data)=>{
})
})