iClient添加管线可点击查询属性弹框显示

1. 加载3维管线服务

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)
			})
		}
	}
}

2. 弹框组件

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 += `
            <div class="prompt-anchor-container">
                <div class="prompt-anchor" style="background:${background} !important;">
                </div>
            </div>
            `;
		}
		// @ts-ignore
		if (this.opt.closeBtn) { // 移动提示框 不显示关闭按钮
			// @ts-ignore
			closeHtml = `<a class="prompt-close" attr="${this.opt.id}" id="prompt-close-${this.opt.id}">x</a>`;
		}
		// @ts-ignore
		let boxShadow = this.opt.style.boxShadow;
		// @ts-ignore
		const promptId = "prompt-" + this.opt.id;
		// @ts-ignore
		const promptConenet = `<div class="prompt-content-container" style="background:${background} !important;color:${color} !important;box-shadow:${boxShadow} !important"><div class="prompt-content" id="prompt-content-${this.opt.id}"></div></div>
                <!-- 锚 -->
                ${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;

3. 添加点击

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()
		}
	}
	
	
}


4. 使用

只注册一个点击事件

  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)=>{

    })
  })
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值