代码迁移

This commit is contained in:
zh
2025-07-03 13:54:01 +08:00
parent b04de8a084
commit 2a4da33e62
985 changed files with 358292 additions and 13 deletions

View File

@ -0,0 +1,25 @@
function html() {
return `
<span class="custom-divider"></span>
<div class="div-item">
<div class="row">
<div class="col">
<span class="label">视点高度</span>
<div class="input-number input-number-unit-1">
<input class="input" type="number" title="" min="0" max="999999" step="0.1" @model="viewPointHeight">
<span class="unit">m</span>
<span class="arrow"></span>
</div>
</div>
</div>
<div class="row">
<div class="col">
<span class="label">采样精度</span>
<input class="input" type="number" title="" min="1" max="100" step="1" @model="precision">
</div>
</div>
</div>
`
}
export { html }

View File

@ -0,0 +1,512 @@
/*
* @Author: Wang jianLei
* @Date: 2022-05-17 21:49:28
* @Last Modified by: Wang JianLei
* @Last Modified time: 2022-05-19 22:08:14
*/
let ViewShed = function (sdk, canvasEleId) {
if (!sdk.viewer) throw new Error("no viewer object!");
alert(canvasEleId)
let canvasEle = document.getElementById(canvasEleId);
if (!canvasEle) throw new Error("the canvas element is not exist");
this.canvasEle = canvasEle;
this.viewer = sdk.viewer;
this.handler = undefined;
this.lightCamera;
this.pyramid;
this.frustumPrimitive;
this.viewershedPolygon;
};
ViewShed.prototype = {
/**
* 初始化handler
*/
initHandler() {
if (this.handler) {
this.handler.destroy();
this.handler = undefined;
}
},
/**
* 开始执行视域分析
* @param {number} precision 精度值越大创建耗时越长建议在10~20之间
*/
createViewshed: function (precision) {
let $this = this;
let scene = $this.viewer.scene;
$this.initHandler();
$this.clearAll();
$this.handler = new Cesium.ScreenSpaceEventHandler($this.viewer.canvas);
$this.handler.setInputAction((event) => {
// 禁止地球旋转和缩放,地球的旋转会对鼠标移动监听有影响,所以需要禁止
scene.screenSpaceCameraController.enableRotate = false;
scene.screenSpaceCameraController.enableZoom = false;
scene.globe.depthTestAgainstTerrain = true;
let earthPosition = scene.pickPosition(event.position);
let pos = $this.cartesian3ToDegree(earthPosition);
$this.handler.setInputAction(function (event) {
let newPosition = scene.pickPosition(event.endPosition);
if (Cesium.defined(newPosition)) {
let pos1 = $this.cartesian3ToDegree(newPosition);
let distance = Cesium.Cartesian3.distance(newPosition, earthPosition);
let angle = $this.getAngle(pos[0], pos[1], pos1[0], pos1[1]);
let pitch = $this.getPitch(earthPosition, newPosition);
$this.ViewShedOptions = {
viewPosition: earthPosition, //观测点 笛卡尔坐标
endPosition: newPosition, //目标点 笛卡尔坐标
direction: angle, //观测方位角 默认为`0`,范围`0~360`
pitch: pitch, //俯仰角,radius,默认为`0`
horizontalViewAngle: 90, //可视域水平夹角,默认为 `90`,范围`0~360`
verticalViewAngle: 60, //可视域垂直夹角,默认为`60`,范围`0~180`
visibleAreaColor: Cesium.Color.GREEN, //可视区域颜色,默认为`green`
invisibleAreaColor: Cesium.Color.RED, //不可见区域颜色,默认为`red`
visualRange: distance, //距离,单位`米`
};
$this.updateViewShed();
}
}, Cesium.ScreenSpaceEventType.MOUSE_MOVE);
}, Cesium.ScreenSpaceEventType.LEFT_DOWN);
$this.handler.setInputAction(() => {
$this.initHandler();
// 开启地球旋转和缩放
scene.screenSpaceCameraController.enableRotate = true;
scene.screenSpaceCameraController.enableZoom = true;
$this.drawViewershed(precision);
}, Cesium.ScreenSpaceEventType.LEFT_UP);
},
ReturnDistance(pos0, pos1) {
let distance = 0;
let point1cartographic = Cesium.Cartographic.fromCartesian(pos0);
let point2cartographic = Cesium.Cartographic.fromCartesian(pos1);
/**根据经纬度计算出距离**/
let geodesic = new Cesium.EllipsoidGeodesic();
geodesic.setEndPoints(point1cartographic, point2cartographic);
let s = geodesic.surfaceDistance;
return s;
},
getHeight(x, y, objectsToExclude) {
let endCartographic = Cesium.Cartographic.fromDegrees(x, y);
let endHeight = this.viewer.scene.sampleHeight(
endCartographic,
objectsToExclude
);
return endHeight;
},
cartesian3ToDegree: function (Cartesian3) {
let _ellipsoid = this.viewer.scene.globe.ellipsoid;
let _cartographic = _ellipsoid.cartesianToCartographic(Cartesian3);
let _lat = Cesium.Math.toDegrees(_cartographic.latitude);
let _lng = Cesium.Math.toDegrees(_cartographic.longitude);
let _alt = _cartographic.height;
return [_lng, _lat, _alt];
},
getAngle: function (lng1, lat1, lng2, lat2) {
let dRotateAngle = Math.atan2(Math.abs(lng1 - lng2), Math.abs(lat1 - lat2));
if (lng2 >= lng1) {
dRotateAngle = lat2 < lat1 ? Math.PI - dRotateAngle : dRotateAngle;
} else {
dRotateAngle =
lat2 >= lat1 ? 2 * Math.PI - dRotateAngle : Math.PI + dRotateAngle;
}
dRotateAngle = (dRotateAngle * 180) / Math.PI;
return dRotateAngle;
},
getPitch(pointA, pointB) {
let transfrom = Cesium.Transforms.eastNorthUpToFixedFrame(pointA);
const vector = Cesium.Cartesian3.subtract(
pointB,
pointA,
new Cesium.Cartesian3()
);
let direction = Cesium.Matrix4.multiplyByPointAsVector(
Cesium.Matrix4.inverse(transfrom, transfrom),
vector,
vector
);
Cesium.Cartesian3.normalize(direction, direction);
return Cesium.Math.PI_OVER_TWO - Cesium.Math.acosClamped(direction.z);
},
updateViewShed: function () {
this.clear();
this.setLightCamera();
this.addVisualPyramid();
this.createFrustum();
},
clear: function () {
if (this.pyramid) {
this.viewer.entities.removeById(this.pyramid.id);
this.pyramid = undefined;
}
if (this.frustumPrimitive) {
this.viewer.scene.primitives.remove(this.frustumPrimitive);
this.frustumPrimitive = undefined;
}
if (this.debugModelMatrixPrimitive) {
this.viewer.scene.primitives.remove(this.debugModelMatrixPrimitive);
this.debugModelMatrixPrimitive = undefined;
}
},
clearAll: function () {
this.clear();
if (this.viewershedPolygon) {
this.viewer.scene.primitives.remove(this.viewershedPolygon);
this.viewershedPolygon = undefined;
}
},
addVisualPyramid: function () {
let options = this.ViewShedOptions;
let position = options.viewPosition;
let visualRange = Number(options.visualRange);
let transform = Cesium.Transforms.eastNorthUpToFixedFrame(position);
this.debugModelMatrixPrimitive = this.viewer.scene.primitives.add(
new Cesium.DebugModelMatrixPrimitive({
modelMatrix: transform,
length: 5.0,
})
);
const halfClock = options.horizontalViewAngle / 2;
const halfCone = options.verticalViewAngle / 2;
const pitch = Cesium.Math.toDegrees(options.pitch);
const ellipsoid = new Cesium.EllipsoidGraphics({
radii: new Cesium.Cartesian3(visualRange, visualRange, visualRange),
minimumClock: Cesium.Math.toRadians(90 - options.direction - halfClock),
maximumClock: Cesium.Math.toRadians(90 - options.direction + halfClock),
minimumCone: Cesium.Math.toRadians(90 - pitch - halfCone),
maximumCone: Cesium.Math.toRadians(90 - pitch + halfCone),
fill: false,
outline: true,
subdivisions: 256,
stackPartitions: 64,
slicePartitions: 64,
outlineColor: Cesium.Color.YELLOWGREEN.withAlpha(0.5),
});
const pyramidEntity = new Cesium.Entity({
position: position,
ellipsoid,
});
this.pyramid = this.viewer.entities.add(pyramidEntity);
},
setLightCamera: function () {
if (!this.lightCamera) {
this.lightCamera = new Cesium.Camera(this.viewer.scene);
}
let options = this.ViewShedOptions;
let visualRange = Number(options.visualRange);
this.lightCamera.position = options.viewPosition;
this.lightCamera.frustum.near = 0.1;
this.lightCamera.frustum.far = visualRange;
const hr = Cesium.Math.toRadians(options.horizontalViewAngle);
const vr = Cesium.Math.toRadians(options.verticalViewAngle);
this.lightCamera.frustum.aspectRatio =
(visualRange * Math.tan(hr / 2) * 2) /
(visualRange * Math.tan(vr / 2) * 2);
this.lightCamera.frustum.fov = hr > vr ? hr : vr;
this.lightCamera.setView({
destination: options.viewPosition,
orientation: {
heading: Cesium.Math.toRadians(options.direction || 0),
pitch: options.pitch || 0,
roll: 0,
},
});
},
createFrustum: function () {
const scratchRight = new Cesium.Cartesian3();
const scratchRotation = new Cesium.Matrix3();
const scratchOrientation = new Cesium.Quaternion();
const direction = this.lightCamera.directionWC;
const up = this.lightCamera.upWC;
let right = this.lightCamera.rightWC;
right = Cesium.Cartesian3.negate(right, scratchRight);
let rotation = scratchRotation;
Cesium.Matrix3.setColumn(rotation, 0, right, rotation);
Cesium.Matrix3.setColumn(rotation, 1, up, rotation);
Cesium.Matrix3.setColumn(rotation, 2, direction, rotation);
let orientation = Cesium.Quaternion.fromRotationMatrix(
rotation,
scratchOrientation
);
let instanceOutline = new Cesium.GeometryInstance({
geometry: new Cesium.FrustumOutlineGeometry({
frustum: this.lightCamera.frustum,
origin: this.ViewShedOptions.viewPosition,
orientation: orientation,
}),
id: "视椎体轮廓线" + Math.random().toString(36).substr(2),
attributes: {
color: Cesium.ColorGeometryInstanceAttribute.fromColor(
new Cesium.Color(0.0, 1.0, 0.0, 1.0)
),
show: new Cesium.ShowGeometryInstanceAttribute(true),
},
});
this.frustumPrimitive = this.viewer.scene.primitives.add(
new Cesium.Primitive({
geometryInstances: instanceOutline,
appearance: new Cesium.PerInstanceColorAppearance({
flat: true,
translucent: false,
closed: true,
}),
})
);
},
createPoint: function (firstPos, secondPos) {
let entity4FirstPos = new Cesium.Entity({
name: "firstPos",
show: true,
position: firstPos,
point: {
show: true,
pixelSize: 20,
color: Cesium.Color.RED,
outlineColor: Cesium.Color.YELLOW,
outlineWidth: 5,
},
description: `
<p>这是绘制的视椎体起点</p>`,
});
this.viewer.entities.add(entity4FirstPos);
let entity4SecondPos = new Cesium.Entity({
name: "secondPos",
show: true,
position: secondPos,
point: {
show: true,
pixelSize: 30,
color: Cesium.Color.YELLOW,
outlineColor: Cesium.Color.RED,
outlineWidth: 8,
},
description: `
<p>这是绘制的视椎体视角终点</p>`,
});
this.viewer.entities.add(entity4SecondPos);
},
//绘制可视域
add(positionArr) {
let polygon = new Cesium.PolygonGeometry({
polygonHierarchy: new Cesium.PolygonHierarchy(
Cesium.Cartesian3.fromDegreesArray(positionArr)
),
height: 0.0,
extrudedHeight: 0.0,
vertexFormat: Cesium.PerInstanceColorAppearance.VERTEX_FORMAT,
stRotation: 0.0, // 纹理的旋转坐标(以弧度为单位),正旋转是逆时针方向
ellipsoid: Cesium.Ellipsoid.WGS84,
granularity: Cesium.Math.RADIANS_PER_DEGREE, // 每个纬度和经度之间的距离(以弧度为单位),确定缓冲区中的位置数
perPositionHeight: false, // 每个位置点使用的高度
closeTop: true,
closeBottom: true,
// NONE 与椭圆表面不符的直线;GEODESIC 遵循测地路径;RHUMB 遵循大黄蜂或恶魔般的道路。
arcType: Cesium.ArcType.GEODESIC, // 多边形边缘线型
});
let polygonInstance = new Cesium.GeometryInstance({
geometry: polygon,
name: "ViewershedPolygon",
attributes: {
color: Cesium.ColorGeometryInstanceAttribute.fromColor(
Cesium.Color.BLUE.withAlpha(0.6)
),
show: new Cesium.ShowGeometryInstanceAttribute(true), //显示或者隐藏
},
});
this.viewershedPolygon = this.viewer.scene.primitives.add(
new Cesium.GroundPrimitive({
geometryInstances: polygonInstance,
appearance: new Cesium.EllipsoidSurfaceAppearance({
aboveGround: true,
material: new Cesium.Material({
fabric: {
type: "Image",
uniforms: {
image: this.returnImgae(),
},
},
}),
}),
})
);
},
drawViewershed(precision) {
const pos = this.cartesian3ToDegree(this.ViewShedOptions.viewPosition);
const radius = this.ViewShedOptions.visualRange;
const direction = this.ViewShedOptions.direction;
let boundary = this.computeBoundaryOptions(pos, radius, direction);
const bbox = boundary.bbox;
let mask = turf.polygon([boundary.boundaryPoints]);
const dis = this.ViewShedOptions.visualRange / (precision * 1000);
let gridPoints = turf.pointGrid(bbox, dis, { mask: mask });
let pointsResult = this.createTargetPoints(gridPoints, dis, pos);
let variogram = kriging.train(
pointsResult.values,
pointsResult.lngs,
pointsResult.lats,
"exponential",
0,
100
);
let grid = kriging.grid([boundary.boundaryPoints], variogram, dis / 1000);
const colors = [
"#ff000080",
"#ff000080",
"#ff000080",
"#ff000080",
"#ff000080",
"#ff000080",
"#00ff0080",
"#00ff0080",
"#00ff0080",
"#00ff0080",
"#00ff0080",
"#00ff0080",
];
this.canvasEle.width = 3840;
this.canvasEle.height = 2160;
kriging.plot(
this.canvasEle,
grid,
[bbox[0], bbox[2]],
[bbox[1], bbox[3]],
colors
);
this.add(boundary.positionArr);
},
computeBoundaryOptions(pos, radius, angle) {
let Ea = 6378137; // 赤道半径
let Eb = 6356725; // 极半径
const lng = pos[0],
lat = pos[1];
const bbox = [lng, lat, lng, lat]; //[minX, minY, maxX, maxY]
let positionArr = [];
let boundaryPoints = [];
positionArr.push(lng, lat);
boundaryPoints.push([lng, lat]);
//正北是0°
let start = angle + 45 > 360 ? angle - 45 - 360 : angle - 45;
let end = start + 90;
for (let i = start; i <= end; i++) {
let dx = radius * Math.sin((i * Math.PI) / 180.0);
let dy = radius * Math.cos((i * Math.PI) / 180.0);
let ec = Eb + ((Ea - Eb) * (90.0 - lat)) / 90.0;
let ed = ec * Math.cos((lat * Math.PI) / 180);
let BJD = lng + ((dx / ed) * 180.0) / Math.PI;
let BWD = lat + ((dy / ec) * 180.0) / Math.PI;
positionArr.push(BJD, BWD);
boundaryPoints.push([BJD, BWD]);
this.refreshBBox(bbox, BJD, BWD);
}
boundaryPoints.push([lng, lat]);
return {
positionArr,
boundaryPoints,
bbox,
};
},
/**
* 更新外围矩形 Bbox
* @param {Array} result 外围矩形Bbox-[minX, minY, maxX, maxY]
* @param {Number} x 经度
* @param {Number} y 纬度
*/
refreshBBox(result, x, y) {
result[0] = x < result[0] ? x : result[0];
result[1] = y < result[1] ? y : result[1];
result[2] = x > result[2] ? x : result[2];
result[3] = y > result[3] ? y : result[3];
},
/**
* 插值点用射线判断通视性
* @param {*} gridPoints 网格点
* @param {*} step 步长,可以理解成是精度
* @param {*} sourcePos 视域分析起点
* @returns kriging插值所需的参数对象{ values:[], lngs:[], lats:[]}
*/
createTargetPoints(gridPoints, step, sourcePos) {
let positionArr = [];
let objectsToExclude = [
this.frustumPrimitive,
this.pyramid,
this.debugModelMatrixPrimitive,
];
let values = [],
lngs = [],
lats = [];
let height = this.getHeight(sourcePos[0], sourcePos[1], objectsToExclude);
positionArr.push({
x: sourcePos[0],
y: sourcePos[1],
z: height,
});
let viewPoint = this.ViewShedOptions.viewPosition;
for (let index = 0; index < gridPoints.features.length; index++) {
const feature = gridPoints.features[index];
const coords = feature.geometry.coordinates;
const x = coords[0],
y = coords[1];
let h = this.getHeight(x, y, objectsToExclude);
let endPoint = Cesium.Cartesian3.fromDegrees(x, y, h);
let direction = Cesium.Cartesian3.normalize(
Cesium.Cartesian3.subtract(
endPoint,
viewPoint,
new Cesium.Cartesian3()
),
new Cesium.Cartesian3()
);
// 建立射线
let ray = new Cesium.Ray(viewPoint, direction);
let result = this.viewer.scene.pickFromRay(ray, objectsToExclude); // 计算交互点,返回第一个
if (result) {
let buffer = this.ReturnDistance(endPoint, result.position);
// let M_color = Cesium.Color.GREEN;
if (buffer > step) {
// M_color = Cesium.Color.RED;
values.push(0);
} else {
values.push(1);
}
lngs.push(x);
lats.push(y);
// this.viewer.entities.add(
// new Cesium.Entity({
// name: "插值点哦",
// show: true,
// position: endPoint,
// point: {
// show: true,
// pixelSize: 10,
// color: M_color,
// outlineWidth: 2,
// outlineColor: Cesium.Color.YELLOW,
// },
// })
// );
}
}
return {
values,
lngs,
lats,
};
},
/**
* canvas转image图片
* @returns base64图片
*/
returnImgae() {
return this.canvasEle.toDataURL("image/png");
},
};
export default ViewShed;

View File

@ -0,0 +1,131 @@
export default `
#define USE_CUBE_MAP_SHADOW true
uniform sampler2D colorTexture;
uniform sampler2D depthTexture;
varying vec2 v_textureCoordinates;
uniform mat4 camera_projection_matrix;
uniform mat4 camera_view_matrix;
uniform samplerCube shadowMap_textureCube;
uniform mat4 shadowMap_matrix;
uniform vec4 shadowMap_lightPositionEC;
uniform vec4 shadowMap_normalOffsetScaleDistanceMaxDistanceAndDarkness;
uniform vec4 shadowMap_texelSizeDepthBiasAndNormalShadingSmooth;
uniform float helsing_viewDistance;
uniform vec4 helsing_visibleAreaColor;
uniform vec4 helsing_invisibleAreaColor;
struct zx_shadowParameters
{
vec3 texCoords;
float depthBias;
float depth;
float nDotL;
vec2 texelStepSize;
float normalShadingSmooth;
float darkness;
};
float czm_shadowVisibility(samplerCube shadowMap, zx_shadowParameters shadowParameters)
{
float depthBias = shadowParameters.depthBias;
float depth = shadowParameters.depth;
float nDotL = shadowParameters.nDotL;
float normalShadingSmooth = shadowParameters.normalShadingSmooth;
float darkness = shadowParameters.darkness;
vec3 uvw = shadowParameters.texCoords;
depth -= depthBias;
float visibility = czm_shadowDepthCompare(shadowMap, uvw, depth);
return czm_private_shadowVisibility(visibility, nDotL, normalShadingSmooth, darkness);
}
vec4 getPositionEC(){
return czm_windowToEyeCoordinates(gl_FragCoord);
}
vec3 getNormalEC(){
return vec3(1.);
}
vec4 toEye(in vec2 uv,in float depth){
vec2 xy=vec2((uv.x*2.-1.),(uv.y*2.-1.));
vec4 posInCamera=czm_inverseProjection*vec4(xy,depth,1.);
posInCamera=posInCamera/posInCamera.w;
return posInCamera;
}
vec3 pointProjectOnPlane(in vec3 planeNormal,in vec3 planeOrigin,in vec3 point){
vec3 v01=point-planeOrigin;
float d=dot(planeNormal,v01);
return(point-planeNormal*d);
}
float getDepth(in vec4 depth){
float z_window=czm_unpackDepth(depth);
z_window=czm_reverseLogDepth(z_window);
float n_range=czm_depthRange.near;
float f_range=czm_depthRange.far;
return(2.*z_window-n_range-f_range)/(f_range-n_range);
}
float shadow(in vec4 positionEC){
vec3 normalEC=getNormalEC();
zx_shadowParameters shadowParameters;
shadowParameters.texelStepSize=shadowMap_texelSizeDepthBiasAndNormalShadingSmooth.xy;
shadowParameters.depthBias=shadowMap_texelSizeDepthBiasAndNormalShadingSmooth.z;
shadowParameters.normalShadingSmooth=shadowMap_texelSizeDepthBiasAndNormalShadingSmooth.w;
shadowParameters.darkness=shadowMap_normalOffsetScaleDistanceMaxDistanceAndDarkness.w;
vec3 directionEC=positionEC.xyz-shadowMap_lightPositionEC.xyz;
float distance=length(directionEC);
directionEC=normalize(directionEC);
float radius=shadowMap_lightPositionEC.w;
if(distance>radius)
{
return 2.0;
}
vec3 directionWC=czm_inverseViewRotation*directionEC;
shadowParameters.depth=distance/radius-0.0003;
shadowParameters.nDotL=clamp(dot(normalEC,-directionEC),0.,1.);
shadowParameters.texCoords=directionWC;
float visibility=czm_shadowVisibility(shadowMap_textureCube,shadowParameters);
return visibility;
}
bool visible(in vec4 result)
{
result.x/=result.w;
result.y/=result.w;
result.z/=result.w;
return result.x>=-1.&&result.x<=1.
&&result.y>=-1.&&result.y<=1.
&&result.z>=-1.&&result.z<=1.;
}
void main(){
// 釉色 = 结构二维(颜色纹理, 纹理坐标)
gl_FragColor = texture2D(colorTexture, v_textureCoordinates);
// 深度 = 获取深度(结构二维(深度纹理, 纹理坐标))
float depth = getDepth(texture2D(depthTexture, v_textureCoordinates));
// 视角 = (纹理坐标, 深度)
vec4 viewPos = toEye(v_textureCoordinates, depth);
// 世界坐标
vec4 wordPos = czm_inverseView * viewPos;
// 虚拟相机中坐标
vec4 vcPos = camera_view_matrix * wordPos;
float near = .001 * helsing_viewDistance;
float dis = length(vcPos.xyz);
if(dis > near && dis < helsing_viewDistance){
// 透视投影
vec4 posInEye = camera_projection_matrix * vcPos;
// 可视区颜色
// vec4 helsing_visibleAreaColor=vec4(0.,1.,0.,.5);
// vec4 helsing_invisibleAreaColor=vec4(1.,0.,0.,.5);
if(visible(posInEye)){
float vis = shadow(viewPos);
if(vis > 0.3){
// gl_FragColor = mix(gl_FragColor,helsing_visibleAreaColor,.5);
} else {
gl_FragColor = mix(gl_FragColor,helsing_invisibleAreaColor,.5);
}
}
}
}`;

View File

@ -0,0 +1,380 @@
// ViewShed.js
import Event from '../../../Event'
import MouseTip from '../../../MouseTip'
import Tools from '../../../Tools'
import EventBinding from '../../Element/Dialog/eventBinding'
import Dialog from '../../../BaseDialog'
import { html } from './_element'
/**
* @constructor
* @description 可视域分析
* @param sdk
* @param {Object} options 选项。
* @param {Number} options.viewPointHeight=1.8 视点高度(m)。
* @param {Number} options.precision=20 精度。
* @param {String} options.visibleAreaColor=#008000 可视区域颜色。
* @param {String} options.invisibleAreaColor=#FF0000 不可视区域颜色。
*/
class CircleViewShed extends Tools {
#intervalEvents = new Map()
constructor(sdk, options = {}, _Dialog = {}) {
super(sdk, options)
this.viewer = sdk.viewer
this.options = {}
this.options.visibleAreaColor = options.visibleAreaColor || '#008000'
this.options.invisibleAreaColor = options.invisibleAreaColor || '#FF0000'
this.ids = []
this.primitives = []
this.viewpointPrimitive = null
this._elms = {}
this.precision = options.precision
this.viewPointHeight = options.viewPointHeight
this.Dialog = _Dialog
this._EventBinding = new EventBinding()
this.html = null
YJ.Analysis.Analyses.push(this)
CircleViewShed.edit(this)
// CircleViewShed.create(this)
}
get viewPointHeight() {
return this.options.viewPointHeight
}
set viewPointHeight(v) {
let viewPointHeight = Math.floor(Number(v) * 10) / 10
if (isNaN(viewPointHeight)) {
viewPointHeight = 1.8
}
if (viewPointHeight < 0) {
viewPointHeight = 0
}
this.options.viewPointHeight = viewPointHeight
this._elms.viewPointHeight &&
this._elms.viewPointHeight.forEach(item => {
item.value = viewPointHeight
})
}
get precision() {
return this.options.precision
}
set precision(v) {
let precision = Math.floor(Number(v))
if (isNaN(precision)) {
precision = 20
} else if (precision < 1) {
precision = 1
}
this.options.precision = precision
this._elms.precision &&
this._elms.precision.forEach(item => {
item.value = precision
})
}
static create(that) {
let count = 0
if (!YJ.Measure.GetMeasureStatus()) {
if (that._DialogObject && that._DialogObject.close) {
that._DialogObject.close()
that._DialogObject = null
}
let Draw = new YJ.Draw.DrawCircle(that.sdk)
Draw.start(async (a, options) => {
// that.center = options.center
if(!options) {
return
}
that.radius = options.radius
let positions = await Cesium.sampleTerrainMostDetailed(
that.sdk.viewer.terrainProvider,
[Cesium.Cartographic.fromDegrees(options.center.lng, options.center.lat)]
);
that.center = {
lng: options.center.lng,
lat: options.center.lat,
alt: positions[0].height
}
await that.analyse()
})
} else {
console.log('上一次测量未结束')
}
}
static async edit(that) {
if (that._DialogObject && that._DialogObject.close) {
that._DialogObject.close()
that._DialogObject = null
}
that._DialogObject = await new Dialog(that.sdk.viewer._container, {
title: '圆形视域分析',
left: '180px',
top: '100px',
closeCallBack: () => {
that.Dialog.closeCallBack && that.Dialog.closeCallBack()
YJ.Measure.SetMeasureStatus(false)
}
})
await that._DialogObject.init()
that._DialogObject._element.body.className =
that._DialogObject._element.body.className + ' circle-view-shed'
let contentElm = document.createElement('div')
contentElm.innerHTML = html()
that._DialogObject.contentAppChild(contentElm)
let drawElm = document.createElement('button')
drawElm.innerHTML = '绘制'
drawElm.addEventListener('click', () => {
let terrainAvailability = that.viewer.terrainProvider.availability;
if (!terrainAvailability) {
window.ELEMENT && window.ELEMENT.Message({
message: '未加载地形数据!',
type: 'warning',
duration: 1500
});
return
}
CircleViewShed.create(that)
})
that._DialogObject.footAppChild(drawElm)
let all_elm = contentElm.getElementsByTagName('*')
that._EventBinding.on(that, all_elm)
that._elms = that._EventBinding.element
}
analyse() {
// this.destroy()
let center = [this.center.lng, this.center.lat]
let radius = this.radius / 1000
let circle = turf.circle(center, radius, {
steps: 180,
units: 'kilometers',
properties: { foo: 'bar' }
})
if (!this.viewpointPrimitive) {
this.viewpointPrimitive = this.viewer.scene.primitives.add(
new Cesium.PointPrimitiveCollection()
)
}
if (!this.viewBillboardPrimitive) {
this.viewBillboardPrimitive = this.viewer.scene.primitives.add(
new Cesium.BillboardCollection()
)
}
let array = []
let distance = radius / this.precision
for (let i = 1; i < circle.geometry.coordinates[0].length; i++) {
let line = turf.lineString([center, circle.geometry.coordinates[0][i]])
let array2 = []
for (let j = 1; j <= this.precision; j++) {
let sliced = turf.lineSliceAlong(line, 0, distance * j, {
units: 'kilometers'
})
array2.push([
sliced.geometry.coordinates[1][0],
sliced.geometry.coordinates[1][1]
])
}
array.push(array2)
}
let viewPoint = Cesium.Cartesian3.fromDegrees(
this.center.lng,
this.center.lat,
this.center.alt + this.viewPointHeight
)
let instances = []
CircleViewShed.getcanvas(this).then(canvas =>
this.viewBillboardPrimitive.add({
position: viewPoint,
image: canvas,
width: 200,
height: 140,
verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
disableDepthTestDistance: Number.POSITIVE_INFINITY
})
)
this.viewpointPrimitive.add({
position: viewPoint,
color: Cesium.Color.AQUA.withAlpha(1),
pixelSize: 6
})
let m = 0
let _this = this
let key = this.randomString()
let intervalEvent = setInterval(() => {
if (m >= array.length) {
let item = this.#intervalEvents.get(key)
item && clearInterval(item.event)
return
}
InBatches(m)
m += 1
}, 0)
this.#intervalEvents.set(key, { event: intervalEvent })
function InBatches(k) {
let instances = []
let i = k
for (let j = 0; j < array[i].length; j++) {
let pt1 = array[i][j]
let pt2
let pt3
let pt4 = array[i][j - 1]
if (i == array.length - 1) {
pt2 = array[0][j]
pt3 = array[0][j - 1]
} else {
pt2 = array[i + 1][j]
pt3 = array[i + 1][j - 1]
}
if (j == 0) {
pt3 = [...center]
pt4 = []
}
let cpt = [(pt1[0] + pt3[0]) / 2, (pt1[1] + pt3[1]) / 2]
let cartographic = Cesium.Cartographic.fromDegrees(cpt[0], cpt[1])
let height = _this.viewer.scene.globe.getHeight(cartographic)
let targetPoint = Cesium.Cartesian3.fromDegrees(cpt[0], cpt[1], height)
let direction = Cesium.Cartesian3.normalize(
Cesium.Cartesian3.subtract(
targetPoint,
viewPoint,
new Cesium.Cartesian3()
),
new Cesium.Cartesian3()
)
let ray = new Cesium.Ray(viewPoint, direction)
let pickedObjects = _this.viewer.scene.drillPickFromRay(
ray,
_this.primitives
)
let result
for (let i = 0; i < pickedObjects.length; i++) {
if (pickedObjects[i].position) {
result = pickedObjects[i]
break
}
}
let color = Cesium.Color.LIME
if (
result &&
Math.abs(result.position.x - targetPoint.x) > 0.01 &&
Math.abs(result.position.y - targetPoint.y) > 0.01 &&
Math.abs(result.position.z - targetPoint.z) > 0.01
) {
color = Cesium.Color.RED
}
let polyline = new Cesium.GroundPolylineGeometry({
positions: Cesium.Cartesian3.fromDegreesArray([
...pt1,
...pt2,
...pt3,
...pt4,
...pt1
]),
width: 2
})
let polygonInstance = new Cesium.GeometryInstance({
geometry: polyline,
name: 'ViewershedPolygon',
attributes: {
color: Cesium.ColorGeometryInstanceAttribute.fromColor(color),
show: new Cesium.ShowGeometryInstanceAttribute(true) //显示或者隐藏
}
})
instances.push(polygonInstance)
}
_this.primitives.push(
_this.viewer.scene.primitives.add(
new Cesium.GroundPolylinePrimitive({
geometryInstances: instances,
appearance: new Cesium.PolylineColorAppearance()
})
)
)
}
}
static getcanvas(that) {
const canvas = document.createElement('canvas')
const ctx = canvas.getContext('2d')
canvas.width = 220
canvas.height = 140
canvas.style.background = '#000000'
let img = new Image()
const data = [
{
images: that.getSourceRootPath() + '/img/bubble/lng.png',
text: '经度:' + parseFloat(that.center.lng.toFixed(10)) + '°'
},
{
images: that.getSourceRootPath() + '/img/bubble/lat.png',
text: '纬度:' + parseFloat(that.center.lat.toFixed(10)) + '°'
},
{
images: that.getSourceRootPath() + '/img/bubble/h.png',
text: '视高:' + that.viewPointHeight + ' m'
},
{
images: that.getSourceRootPath() + '/img/bubble/radius.png',
text: '半径:' + that.radius + ' m'
}
]
img.src = that.getSourceRootPath() + '/img/bubble/bubble.png'
let imagesLoaded = 0
return new Promise(async (resolve, reject) => {
img.onload = () => {
ctx.drawImage(img, 0, 0, canvas.width, canvas.height)
data.forEach((item, index) => {
const img = new Image()
img.src = item.images
img.onload = () => {
ctx.drawImage(img, 12, 12 + index * 26)
ctx.fillStyle = '#fff'
ctx.font = '12px Arial'
ctx.fillText(item.text, 44, 28 + index * 26)
imagesLoaded++
if (imagesLoaded === data.length) {
resolve(canvas)
}
}
})
}
})
}
destroy() {
for (const [key, value] of this.#intervalEvents) {
clearInterval(value.event)
}
this.#intervalEvents = new Map()
for (let i = 0; i < this.primitives.length; i++) {
this.viewer.scene.primitives.remove(this.primitives[i])
}
this.primitives = []
if (this.viewpointPrimitive) {
this.viewer.scene.primitives.remove(this.viewpointPrimitive)
this.viewpointPrimitive = null
}
if (this.viewBillboardPrimitive) {
this.viewer.scene.primitives.remove(this.viewBillboardPrimitive)
this.viewBillboardPrimitive = null
}
YJ.Measure.SetMeasureStatus(false)
}
}
export default CircleViewShed

View File

@ -0,0 +1,208 @@
import Tools from '../../../Tools';
class ContourAnalysis {
/**
* @constructor 等高线分析
* @param sdk
* **/
constructor(sdk, options = {}) {
this.viewer = sdk.viewer
let terrainAvailability = this.viewer.terrainProvider.availability;
if (!terrainAvailability) {
this.error = '未加载地形数据!'
window.ELEMENT && window.ELEMENT.Message({
message: '未加载地形数据!',
type: 'warning',
duration: 1500
});
console.warn(this.error)
return
}
this.positions = options.positions
this.interfaceNum = options.interfaceNum || 25 //内插时均分的数量即沿着边界长或宽均分成n分进行插点默认值25
this.colorFill = options.colorFill || [
"#8CEA00",
"#B7FF4A",
"#FFFF37",
"#FFE66F",
"#FFD1A4",
"#FFCBB3",
"#FFBD9D",
"#FFAD86",
"#FF9D6F",
"#FF8F59",
"#FF8040",
"#FF5809",
"#F75000",
"#D94600",
"#BB3D00",
"#A23400",
"#842B00",
"#642100",
"#4D0000",
"#2F0000",
]; //等高线赋值颜色内含default值
this.countorLineList = Cesium.defaultValue(options.countorLineList, []);
YJ.Analysis.Analyses.push(this)
this.createNewLine();
}
createNewLine() {
ContourAnalysis.interpolatePoint(this);
}
//利用turf在box内进行插点
static interpolatePoint(that) {
let curPoints = that.positions
let features = [];
const boundaryCoord = {
minX: 360,
maxX: -360,
minY: 180,
maxY: -180,
}; //绘制几何图形的外围矩形box
for (let index = 0; index < curPoints.length; index++) {
const element = Cesium.Cartesian3.fromDegrees(curPoints[index].lng, curPoints[index].lat, curPoints[index].alt);
let ellipsoid = that.viewer.scene.globe.ellipsoid;
let cartographic = ellipsoid.cartesianToCartographic(element);
let lat = Cesium.Math.toDegrees(cartographic.latitude);
let lng = Cesium.Math.toDegrees(cartographic.longitude);
boundaryCoord.maxY = Math.max(lat, boundaryCoord.maxY);
boundaryCoord.minY = Math.min(lat, boundaryCoord.minY);
boundaryCoord.maxX = Math.max(lng, boundaryCoord.maxX);
boundaryCoord.minX = Math.min(lng, boundaryCoord.minX);
let curFeature = {
type: "Feature",
properties: {},
geometry: {
type: "Point",
coordinates: [lng, lat],
},
};
features.push(curFeature);
}
let boundaryJson = {
type: "FeatureCollection",
features: features,
};
turf.featureEach(boundaryJson, function (point) {
point.properties.height = 0;
});
let options = {
gridType: "points",
property: "height",
units: "kilometers",
};
let from = turf.point([boundaryCoord.minX, boundaryCoord.minY]);
let to = turf.point([boundaryCoord.maxX, boundaryCoord.maxY]);
let diagonalDistance = turf.rhumbDistance(from, to, {
units: "kilometers",
});
let grid = turf.interpolate(
boundaryJson,
diagonalDistance / that.interfaceNum,
options
);
let minHeight = 10000000; //最低点高程值
let maxHeight = -100000000; //最高点高程值
turf.featureEach(grid, function (point) {
let pos = point.geometry.coordinates;
let cartographic = Cesium.Cartographic.fromDegrees(pos[0], pos[1]);
let height = that.viewer.scene.globe.getHeight(cartographic);
maxHeight = Math.max(height, maxHeight);
minHeight = Math.min(height, minHeight);
point.properties.height = height;
});
let breaks = [];
let stepCount = that.colorFill.length - 1;
let step = (maxHeight - minHeight) / stepCount;
for (let index = 0; index < stepCount + 1; index++) {
breaks.push(Math.ceil(minHeight + step * index));
}
// console.log('grid', grid)
let linesJson = turf.isolines(grid, breaks, { zProperty: "height" });
let _countorLine = Cesium.GeoJsonDataSource.load(linesJson, {
clampToGround: true,
});
// console.log(linesJson)
_countorLine.then(function (dataSource) {
console.log(dataSource)
that.countorLine = dataSource; //最终计算生成的等高线对象GeoJsonDataSource
that.countorLineList.push(dataSource); //等高线数组
that.viewer.dataSources.add(dataSource);
let entities = dataSource.entities.values;
for (let index = 0; index < entities.length; index++) {
const element = entities[index];
let center = getPolylineCenter(element.polyline);
element.position = center;
// dataSource.entities.add(new Cesium.Entity({
// position: center,
// label: {
// text: element.properties.height._value + '',
// font: '20px Microsoft YaHei',
// fillColor: Cesium.Color.fromCssColorString('#f1d20c'),
// style: Cesium.LabelStyle.FILL_AND_OUTLINE,
// disableDepthTestDistance: Number.POSITIVE_INFINITY,
// heightReference: Cesium.HeightReference.CLAMP_TO_GROUND
// },
// }))
// element.label = new Cesium.LabelGraphics({
// })
let cur_index = that.getObjectIndex(
breaks,
element.properties.height._value
);
if (cur_index) {
element.polyline.material = Cesium.Color.fromCssColorString(
that.colorFill[cur_index - 1]
);
}
}
});
function getPolylineCenter(polyline) {
let tools = new Tools()
let positions = polyline.positions;
let length = positions._value.length;
let array = []
for (let i = 0; i < length; i++) {
let pos = tools.cartesian3Towgs84(positions._value[i], that.viewer)
array.push([pos.lng, pos.lat])
}
let line = turf.lineString(array);
let distance = turf.length(line, { units: "kilometers" });
let along = turf.along(line, distance/2, { units: "kilometers" });
return Cesium.Cartesian3.fromDegrees(along.geometry.coordinates[0], along.geometry.coordinates[1], 0);
}
}
/**
* 返回随机插入的数在数组中的位置
* @param {*} arr 元数组
* @param {*} num 随机数
* @returns 序号
* @example getObjectIndex([0,218,325,333,444],354)=>4;
*/
getObjectIndex(arr, num) {
for (let i = 0; i < arr.length; i++) {
if (arr[i] > num) {
return i;
}
}
}
clear(countorLine) {
if (countorLine) {
this.viewer.dataSources.remove(countorLine);
let index = this.countorLineList.indexOf(countorLine);
this.countorLineList.splice(index, 1);
}
}
destroy() {
this.countorLineList.forEach((element) => {
this.viewer.dataSources.remove(element);
});
this.countorLineList = [];
}
}
export default ContourAnalysis;

View File

@ -0,0 +1,125 @@
class CreatePolygon {
constructor(viewer) {
if (!viewer) throw new Error("no viewer object!");
this.activePoints = [];
this.viewer = viewer;
this.handler = undefined;
this.init();
}
init() {
this.activeShapePoints = [];
this.floatingPoint = undefined;
this.activeShape = undefined;
this.activePoints.forEach((element) => {
this.viewer.entities.remove(element);
});
this.activePoints = [];
this.initHandler();
}
start(callback) {
const $this = this;
$this.keyDownStatus(true);
$this.init();
$this.handler = new Cesium.ScreenSpaceEventHandler($this.viewer.canvas);
$this.handler.setInputAction(function (event) {
let earthPosition = $this.viewer.scene.pickPosition(event.position);
if (Cesium.defined(earthPosition)) {
if ($this.activeShapePoints.length === 0) {
$this.floatingPoint = $this.createPoint(earthPosition);
$this.activeShapePoints.push(earthPosition);
let dynamicPositions = new Cesium.CallbackProperty(function () {
return new Cesium.PolygonHierarchy($this.activeShapePoints);
}, false);
$this.activeShape = $this.drawShape(dynamicPositions); //绘制动态图
}
$this.activeShapePoints.push(earthPosition);
$this.createPoint(earthPosition);
}
}, Cesium.ScreenSpaceEventType.LEFT_CLICK);
$this.handler.setInputAction(function (event) {
if (Cesium.defined($this.floatingPoint)) {
let newPosition = $this.viewer.scene.pickPosition(event.endPosition);
if (Cesium.defined(newPosition)) {
$this.floatingPoint.position.setValue(newPosition);
$this.activeShapePoints.pop();
$this.activeShapePoints.push(newPosition);
}
}
}, Cesium.ScreenSpaceEventType.MOUSE_MOVE);
$this.handler.setInputAction(function () {
$this.activeShapePoints.pop(); //去除最后一个动态点
if ($this.activeShapePoints.length) {
$this.polygon = $this.drawShape($this.activeShapePoints); //绘制最终图
}
$this.viewer.entities.remove($this.floatingPoint); //去除动态点图形(当前鼠标点)
$this.viewer.entities.remove($this.activeShape); //去除动态图形
$this.activePoints.forEach((element) => {
$this.viewer.entities.remove(element);
});
$this.handler.destroy();
setTimeout(() => {
if (typeof callback == "function") callback();
}, 1000);
}, Cesium.ScreenSpaceEventType.RIGHT_CLICK);
}
initHandler() {
if (this.handler) {
this.handler.destroy();
this.handler = undefined;
}
}
createPoint(worldPosition) {
let point = this.viewer.entities.add({
position: worldPosition,
point: {
color: Cesium.Color.SKYBLUE,
pixelSize: 5,
},
});
this.activePoints.push(point);
return point;
}
drawShape(positionData) {
let shape = this.viewer.entities.add({
polygon: {
hierarchy: positionData,
material: new Cesium.ColorMaterialProperty(
Cesium.Color.BLUE.withAlpha(0.4)
),
zIndex: 99999999
},
});
return shape;
}
//快捷键//Ctrl + Z
keyDownStatus(bool) {
const $this = this;
document.onkeydown = function (event) {
if (event.ctrlKey && window.event.keyCode == 90) {
if (!bool) {
return false;
}
$this.activeShapePoints.pop();
$this.viewer.entities.remove(
$this.activePoints[$this.activePoints.length - 1]
);
$this.activePoints.pop();
}
};
}
/**
* Cesium中世界坐标系笛卡尔转经纬度
* @param {*} cartesian3
* @returns 经纬度
*/
Cartesian3ToDgrees(cartesian3) {
let cartographic =
window.viewer.scene.globe.ellipsoid.cartesianToCartographic(cartesian3);
let lat = Cesium.Math.toDegrees(cartographic.latitude);
let lng = Cesium.Math.toDegrees(cartographic.longitude);
let alt = cartographic.height;
return { lng: lng, lat: lat, alt: alt };
}
}
export default CreatePolygon;

View File

@ -0,0 +1,75 @@
function html() {
return `
<span class="custom-divider"></span>
<div class="div-item">
<div class="row">
<div class="col">
<span class="label" style="flex: 0 0 70px;">绘制分析区域</span>
<button class="draw-btn"><svg class="icon-edit"><use xlink:href="#yj-icon-edit"></use></svg>开始绘制</button>
</div>
</div>
</div>
<span class="custom-divider"></span>
<div class="div-item">
<div class="row">
<div class="col">
<span class="label">基准高度</span>
<div class="input-number input-number-unit-1">
<input class="input" type="number" title="" min="-999999" max="999999" name="height">
<span class="unit">m</span>
<span class="arrow"></span>
</div>
</div>
<div class="col">
<span class="label">精度</span>
<div class="input-number input-number-unit">
<input class="input" type="number" title="" min="1" max="1250" name="precision">
<span class="arrow"></span>
</div>
</div>
</div>
</div>
<span class="custom-divider"></span>
<div class="div-item">
<div class="row">
<div class="col">
<span class="label" style="flex: 0 0 74px;">总分析面积:</span>
<span class="text-number" name="allArea">0</span>
<span class="unit text-number">m²</span>
</div>
<div class="col">
<span class="label" style="flex: 0 0 90px;">无须填挖面积:</span>
<span class="text-number" name="noArea">0</span>
<span class="unit text-number">m²</span>
</div>
</div>
<div class="row">
<div class="col">
<span class="label" style="flex: 0 0 74px;">填方面积:</span>
<span class="text-number" name="fillArea">0</span>
<span class="unit text-number">m²</span>
</div>
<div class="col">
<span class="label" style="flex: 0 0 90px;">挖方面积:</span>
<span class="text-number" name="cutArea">0</span>
<span class="unit text-number">m²</span>
</div>
</div>
<div class="row">
<div class="col">
<span class="label" style="flex: 0 0 74px;">填方体积:</span>
<span class="text-number" name="fillVolume">0</span>
<span class="unit text-number">m³</span>
</div>
<div class="col">
<span class="label" style="flex: 0 0 90px;">挖方体积:</span>
<span class="text-number" name="cutVolume">0</span>
<span class="unit text-number">m³</span>
</div>
</div>
</div>
<span class="custom-divider"></span>
`
}
export { html }

View File

@ -0,0 +1,354 @@
import Dialog from '../../../BaseDialog';
import { html } from "./_element";
// import CreatePolygon from "./CreatePolygon";
import DrawPolygon from "../../../Draw/drawPolygon"
class CutFillAnalysis {
/**
* @constructor 填挖方分析
* @param sdk
* **/
constructor(sdk, options = {}, _Dialog = {}) {
this.sdk = sdk;
this.viewer = sdk.viewer;
// if (!positions) throw new Error("no positions object!");
// this.positions = positions;
this.height = options.height || 70
this.maxHeigh = -1000000;
this.precision = options.precision || 125
this.Dialog = _Dialog
this.result = {
allArea: "",
cutArea: "",
cutVolume: "",
fillArea: "",
fillVolume: "",
noArea: "",
}
this.entities = []
this.Draw = new DrawPolygon(this.sdk)
YJ.Analysis.Analyses.push(this)
CutFillAnalysis.EditBox(this)
}
create() {
this.clean()
this.Draw.start((a, positions) => {
if(!positions || positions.length<3) {
let _error = '最少需要三个坐标!'
console.warn(_error)
window.ELEMENT && window.ELEMENT.Message({
message: _error,
type: 'warning',
duration: 1500
});
return
}
let fromDegreesArray = []
for (let i = 0; i < positions.length; i++) {
fromDegreesArray.push(positions[i].lng, positions[i].lat, positions[i].alt)
}
this.positions = Cesium.Cartesian3.fromDegreesArrayHeights(fromDegreesArray)
this.createPolygonGeo(this.positions);
this.result = this.VolumeAnalysis();
this.viewer.scene.screenSpaceCameraController.enableCollisionDetection = false; //允许相机进入地下
})
// const $this = this;
// if (!this.cp) {
// this.cp = new CreatePolygon(this.viewer)
// }
// this.cp.start(function () {
// console.log($this.cp.activeShapePoints)
// $this.positions = $this.cp.activeShapePoints;
// $this.createPolygonGeo($this.positions);
// $this.result = $this.VolumeAnalysis();
// $this.viewer.entities.remove($this.cp.polygon);
// $this.viewer.scene.screenSpaceCameraController.enableCollisionDetection = false; //允许相机进入地下
// });
}
createPolygonGeo(points) {
//计算网格粒度-精度
let granularity = Math.PI / Math.pow(2, 11);
granularity = granularity / this.precision;
let polygonGeometry = new Cesium.PolygonGeometry.fromPositions({
positions: points,
vertexFormat: Cesium.PerInstanceColorAppearance.FLAT_VERTEX_FORMAT,
granularity: granularity,
});
//创建自定义平面几何体
this.geom = new Cesium.PolygonGeometry.createGeometry(polygonGeometry);
}
VolumeAnalysis() {
let cutArea = 0,
cutVolume = 0,
fillArea = 0,
fillVolume = 0,
noArea = 0;
const indices = this.geom.indices; //获取顶点索引数据
if (!this.geom || !this.geom.attributes || !this.geom.attributes.position) {
return;
}
const positions = this.geom.attributes.position.values;
for (let index = 0; index < indices.length; index += 3) {
const pos0 = this.returnPosition(positions, indices[index]);
const pos1 = this.returnPosition(positions, indices[index + 1]);
const pos2 = this.returnPosition(positions, indices[index + 2]);
let entity = this.viewer.entities.add({
name: "三角面",
polygon: {
hierarchy: [pos0.heightPos, pos1.heightPos, pos2.heightPos],
perPositionHeight: true,
material: Cesium.Color.fromRandom(),
extrudedHeight: this.height,
outline: true,
outlineColor: Cesium.Color.BLACK,
},
});
this.entities.push(entity)
//水平状态下三角形面积
const area = this.computeArea4Triangle(
pos0.noHeightPos,
pos1.noHeightPos,
pos2.noHeightPos
);
//计算三个点的均高
const height = (pos0.height + pos1.height + pos2.height) / 3;
if (height < this.height) {
// 需要填方的部分
fillArea += area;
const volume = area * (this.height - height);
fillVolume += volume;
} else if (height == this.height) {
noArea += area;
} else {
// 需要挖方的部分
cutArea += area;
const volume = area * (height - this.height);
cutVolume += volume;
}
}
const allArea = cutArea + fillArea + noArea;
// this.result = {
// allArea,
// cutArea,
// cutVolume,
// fillArea,
// fillVolume,
// noArea,
// };
this.result.allArea = allArea
this.result.cutArea = cutArea
this.result.cutVolume = cutVolume
this.result.fillArea = fillArea
this.result.fillVolume = fillVolume
this.result.noArea = noArea
return this.result;
}
computeCentroid4Polygon(positions) {
let x = [],
y = [];
let allX = 0,
allY = 0;
for (let i = 0; i < positions.length; i++) {
let cartographic = Cesium.Cartographic.fromCartesian(positions[i]);
allX += cartographic.longitude;
allY += cartographic.latitude;
x.push(cartographic.longitude);
y.push(cartographic.latitude);
}
let centroidx = allX / positions.length;
let centroidy = allY / positions.length;
const Cartographic = new Cesium.Cartographic(centroidx, centroidy);
return Cesium.Cartesian3.fromRadians(
Cartographic.longitude,
Cartographic.latitude,
this.maxHeigh + 30
);
}
/**
* 海伦公式求取三角形面积
* @param {*} pos1
* @param {*} pos2
* @param {*} pos3
* @returns 三角形面积㎡
*/
computeArea4Triangle(pos1, pos2, pos3) {
let a = Cesium.Cartesian3.distance(pos1, pos2);
let b = Cesium.Cartesian3.distance(pos2, pos3);
let c = Cesium.Cartesian3.distance(pos3, pos1);
let S = (a + b + c) / 2;
return Math.sqrt(S * (S - a) * (S - b) * (S - c));
}
returnPosition(positions, index) {
let cartesian = new Cesium.Cartesian3(
positions[index * 3],
positions[index * 3 + 1],
positions[index * 3 + 2]
);
let cartographic = Cesium.Cartographic.fromCartesian(cartesian);
let height = this.viewer.scene.sampleHeightSupported
? this.viewer.scene.sampleHeight(cartographic)
: this.viewer.scene.globe.getHeight(cartographic);
if (height > this.maxHeigh) {
this.maxHeigh = height;
}
return {
heightPos: Cesium.Cartesian3.fromRadians(
cartographic.longitude,
cartographic.latitude,
height
),
noHeightPos: Cesium.Cartesian3.fromRadians(
cartographic.longitude,
cartographic.latitude,
0
),
height: height,
};
}
static async EditBox(that) {
if (that._DialogObject && that._DialogObject.close) {
that._DialogObject.close()
that._DialogObject = null
}
that._DialogObject = await new Dialog(that.sdk.viewer._container, {
title: '土方分析', left: '180px', top: '100px',
closeCallBack: () => {
that.clean()
that.Dialog.closeCallBack && that.Dialog.closeCallBack()
},
})
await that._DialogObject.init()
let contentElm = document.createElement('div');
contentElm.innerHTML = html()
that._DialogObject.contentAppChild(contentElm)
that._DialogObject._element.body.className = that._DialogObject._element.body.className + ' cut-fill'
// 高度值
let e_height = contentElm.querySelector("input[name='height']")
e_height.value = that.height
e_height.addEventListener('blur', (e) => {
let value = e.target.value
if (e.data != '.' && (e.data != '-' || e.target.value)) {
value = Number(value)
if ((e.target.max) && value > Number(e.target.max)) {
value = Number(e.target.max)
}
if ((e.target.min) && value < Number(e.target.min)) {
value = Number(e.target.min)
}
e_height.value = value
that.height = e_height.value;
}
});
// 精度值
let e_precision = contentElm.querySelector("input[name='precision']")
e_precision.value = that.precision
e_precision.addEventListener('blur', (e) => {
let value = Number(e.target.value)
if ((e.target.max) && value > Number(e.target.max)) {
value = Number(e.target.max)
}
if ((e.target.min) && value < Number(e.target.min)) {
value = Number(e.target.min)
}
e_precision.value = value
that.precision = e_precision.value;
});
// 总分析面积
let e_allArea = contentElm.querySelector("span[name='allArea']")
e_allArea.innerHTML = that.result.allArea || 0
Object.defineProperty(that.result, 'allArea', {
get() {
return e_allArea.innerHTML
},
set(value) {
e_allArea.innerHTML = Number(value.toFixed(4))
}
})
// 填方面积
let e_fillArea = contentElm.querySelector("span[name='fillArea']")
e_fillArea.innerHTML = that.result.fillArea || 0
Object.defineProperty(that.result, 'fillArea', {
get() {
return e_fillArea.innerHTML
},
set(value) {
e_fillArea.innerHTML = Number(value.toFixed(4))
}
})
// 填方体积
let e_fillVolume = contentElm.querySelector("span[name='fillVolume']")
e_fillVolume.innerHTML = that.result.fillVolume || 0
Object.defineProperty(that.result, 'fillVolume', {
get() {
return e_fillVolume.innerHTML
},
set(value) {
e_fillVolume.innerHTML = Number(value.toFixed(4))
}
})
// 挖方面积
let e_cutArea = contentElm.querySelector("span[name='cutArea']")
e_cutArea.innerHTML = that.result.cutArea || 0
Object.defineProperty(that.result, 'cutArea', {
get() {
return e_cutArea.innerHTML
},
set(value) {
e_cutArea.innerHTML = Number(value.toFixed(4))
}
})
// 挖方体积
let e_cutVolume = contentElm.querySelector("span[name='cutVolume']")
e_cutVolume.innerHTML = that.result.cutVolume || 0
Object.defineProperty(that.result, 'cutVolume', {
get() {
return e_cutVolume.innerHTML
},
set(value) {
e_cutVolume.innerHTML = Number(value.toFixed(4))
}
})
// 无须填挖面积
let e_noArea = contentElm.querySelector("span[name='noArea']")
e_noArea.innerHTML = that.result.noArea || 0
Object.defineProperty(that.result, 'noArea', {
get() {
return e_noArea.innerHTML
},
set(value) {
e_noArea.innerHTML = Number(value.toFixed(4))
}
})
let newDivBtn = contentElm.getElementsByClassName('draw-btn')[0];
newDivBtn.addEventListener('click', () => {
that.create()
});
}
clean() {
this.Draw && this.Draw.end()
for (let i = 0; i < this.entities.length; i++) {
this.viewer.entities.remove(this.entities[i])
}
}
destroy() {
this.clean()
if (this._DialogObject && this._DialogObject.close) {
this._DialogObject.close()
this._DialogObject = null
}
}
}
export default CutFillAnalysis;

View File

@ -0,0 +1,483 @@
import Base from "../../Base/index";
import Dialog from '../../../BaseDialog'
import { setActiveViewer, closeRotateAround, closeViewFollow} from '../../../Global/global'
let FlatList = {}
class Flat extends Base {
/**
* @constructor
* @description 模型压平
* @param sdk
* @param {Cesium.Cesium3DTileset} tileset 三维模型
* @param {Object} options
* @param {string} attr.id id
* @param {Cesium.Cartesian3[]} attr.positions 压平面坐标
*/
constructor(sdk, tileset, options = {}, _Dialog = {}) {
super(sdk)
if (!tileset || !this.sdk || !this.sdk.viewer) return;
this.options = { ...options }
this.options.id = options.id || this.randomString()
this.options.name = options.name || '压平面'
this.options.positions = options.positions || []
this.options.show = (options.show || options.show === false) ? options.show : true
this.tileset = tileset;
this.Dialog = _Dialog
if (!this.options.height && this.options.height !== 0) {
let height = this.options.positions[0].alt
for (let i = 0; i < this.options.positions.length; i++) {
if (height > this.options.positions[i].alt) {
height = this.options.positions[i].alt
}
}
this.options.height = height
}
if (FlatList[this.tileset.id]) {
FlatList[this.tileset.id].push({ ...this.options })
}
else {
FlatList[this.tileset.id] = [{ ...this.options }]
}
this.center = tileset.boundingSphere.center.clone();
this.center84 = this.cartesian3Towgs84(this.center, this.sdk.viewer)
this.matrix = Cesium.Transforms.eastNorthUpToFixedFrame(this.center.clone());
this.localMatrix = Cesium.Matrix4.inverse(this.matrix, new Cesium.Matrix4());
// this.entity = {
// id: this.options.id
// }
this.addFlat()
// Flat.createPolygon(this)
}
get show() {
return this.options.show
}
set show(v) {
this.options.show = v
for (let i = 0; i < FlatList[this.tileset.id].length; i++) {
if (FlatList[this.tileset.id][i].id == this.options.id) {
FlatList[this.tileset.id][i].show = v
}
}
this.addFlat()
}
get height() {
return this.options.height
}
set height(v) {
this.options.height = Number(v)
for (let i = 0; i < FlatList[this.tileset.id].length; i++) {
if (FlatList[this.tileset.id][i].id == this.options.id) {
FlatList[this.tileset.id][i].height = Number(v)
}
}
this.addFlat()
}
get name() {
return this.options.name
}
set name(v) {
this.options.name = v
for (let i = 0; i < FlatList[this.tileset.id].length; i++) {
if (FlatList[this.tileset.id][i].id == this.options.id) {
FlatList[this.tileset.id][i].name = v
}
}
}
addFlat() {
let localPositionsArr = []
for (let i = 0; i < FlatList[this.tileset.id].length; i++) {
let item = FlatList[this.tileset.id][i];
if (item.show) {
const positions = item.positions;
let height = item.height
let fromDegreesArray = []
for (let i = 0; i < positions.length; i++) {
fromDegreesArray.push(positions[i].lng, positions[i].lat)
}
FlatList[this.tileset.id][i].flatHeight = height - this.center84.alt
let localCoor = this.cartesiansToLocal(Cesium.Cartesian3.fromDegreesArray(fromDegreesArray));
localPositionsArr.push(localCoor);
}
}
const funstr = this.getIsinPolygonFun(localPositionsArr);
let str = ``;
for (let i = 0; i < localPositionsArr.length; i++) {
const coors = localPositionsArr[i];
const n = coors.length;
let instr = ``;
coors.forEach((coordinate, index) => {
instr += `points_${n}[${index}] = vec2(${coordinate[0]}, ${coordinate[1]});\n`;
})
str += `
${instr}
if(isPointInPolygon_${n}(position2D)){
vec4 tileset_local_position_transformed = vec4(tileset_local_position.x, tileset_local_position.y, ground_z + ${FlatList[this.tileset.id][i].flatHeight}, 1.0);
vec4 model_local_position_transformed = czm_inverseModel * u_tileset_localToWorldMatrix * tileset_local_position_transformed;
vsOutput.positionMC.xy = model_local_position_transformed.xy;
vsOutput.positionMC.z = model_local_position_transformed.z+ modelMC.z*0.002;
return;
}`;
}
this.updateShader(funstr, str);
}
// static createPolygon(that) {
// let color = '#ffffff'
// let linecolor = '#000000'
// let positions = that.options.positions
// let fromDegreesArray = []
// for (let i = 0; i < positions.length; i++) {
// fromDegreesArray.push(positions[i].lng, positions[i].lat, that.options.height)
// }
// that.positions = Cesium.Cartesian3.fromDegreesArrayHeights(fromDegreesArray)
// that.entity = that.sdk.viewer.entities.add({
// show: that.options.show,
// id: that.options.id,
// polyline: {
// positions: [...that.positions, that.positions[0], that.positions[1]],
// width: 2,
// material: Cesium.Color.fromCssColorString(linecolor),
// depthFailMaterial: new Cesium.PolylineDashMaterialProperty({
// color: Cesium.Color.YELLOW
// }),
// clampToGround: false,
// zIndex: that.sdk._entityZIndex
// },
// })
// that.sdk._entityZIndex++
// }
remove() {
FlatList[this.tileset.id] = FlatList[this.tileset.id].filter((attr) => {
return attr.id != this.options.id;
})
let localPositionsArr = [];
for (let i = 0; i < FlatList[this.tileset.id].length; i++) {
let item = FlatList[this.tileset.id][i];
if (item.show) {
const positions = item.positions;
let height = item.height
let fromDegreesArray = []
for (let i = 0; i < positions.length; i++) {
fromDegreesArray.push(positions[i].lng, positions[i].lat)
}
FlatList[this.tileset.id][i].flatHeight = height - this.center84.alt
let localCoor = this.cartesiansToLocal(Cesium.Cartesian3.fromDegreesArray(fromDegreesArray));
localPositionsArr.push(localCoor);
}
}
const funstr = this.getIsinPolygonFun(localPositionsArr);
let str = ``;
for (let i = 0; i < localPositionsArr.length; i++) {
const coors = localPositionsArr[i];
const n = coors.length;
let instr = ``;
coors.forEach((coordinate, index) => {
instr += `points_${n}[${index}] = vec2(${coordinate[0]}, ${coordinate[1]});\n`;
})
str += `
${instr}
if(isPointInPolygon_${n}(position2D)){
vec4 tileset_local_position_transformed = vec4(tileset_local_position.x, tileset_local_position.y, ground_z + ${FlatList[this.tileset.id][i].flatHeight}, 1.0);
vec4 model_local_position_transformed = czm_inverseModel * u_tileset_localToWorldMatrix * tileset_local_position_transformed;
vsOutput.positionMC.xy = model_local_position_transformed.xy;
vsOutput.positionMC.z = model_local_position_transformed.z+ modelMC.z*0.002;
return;
}`;
}
this.updateShader(funstr, str);
}
// 根据数组长度,构建 判断点是否在面内 的压平函数
getIsinPolygonFun(polygons) {
let pmap = polygons.map((polygon) => polygon.length);
let uniqueArray = this.getUniqueArray(pmap);
let str = ``;
uniqueArray.forEach(length => {
str += `
vec2 points_${length}[${length}];
bool isPointInPolygon_${length}(vec2 point){
int nCross = 0; // 交点数
const int n = ${length};
for(int i = 0; i < n; i++){
vec2 p1 = points_${length}[i];
vec2 p2 = points_${length}[int(mod(float(i+1),float(n)))];
if(p1[1] == p2[1]){
continue;
}
if(point[1] < min(p1[1], p2[1])){
continue;
}
if(point[1] >= max(p1[1], p2[1])){
continue;
}
float x = p1[0] + ((point[1] - p1[1]) * (p2[0] - p1[0])) / (p2[1] - p1[1]);
if(x > point[0]){
nCross++;
}
}
return int(mod(float(nCross), float(2))) == 1;
}
`
})
return str
}
updateShader(vtx1, vtx2) {
let flatCustomShader = new Cesium.CustomShader({
uniforms: {
u_tileset_localToWorldMatrix: {
type: Cesium.UniformType.MAT4,
value: this.matrix,
},
u_tileset_worldToLocalMatrix: {
type: Cesium.UniformType.MAT4,
value: this.localMatrix,
},
u_flatHeight: {
type: Cesium.UniformType.FLOAT,
value: this.flatHeight,
},
},
vertexShaderText: `
// 所有isPointInPolygon函数
${vtx1}
void vertexMain(VertexInput vsInput, inout czm_modelVertexOutput vsOutput){
vec3 modelMC = vsInput.attributes.positionMC;
vec4 model_local_position = vec4(modelMC.x, modelMC.y, modelMC.z, 1.0);
vec4 tileset_local_position = u_tileset_worldToLocalMatrix * czm_model * model_local_position;
vec2 position2D = vec2(tileset_local_position.x,tileset_local_position.y);
float ground_z = 0.0;
// 多个多边形区域
${vtx2}
}`,
});
this.tileset.customShader = flatCustomShader;
this.sdk.viewer.scene.requestRender();
}
// 数组去重,不能处理嵌套的数组
getUniqueArray = (arr) => {
return arr.filter(function (item, index, arr) {
//当前元素,在原始数组中的第一个索引==当前索引值,否则返回当前元素
return arr.indexOf(item, 0) === index;
});
}
// 世界坐标转数组局部坐标
cartesiansToLocal(positions) {
let arr = [];
for (let i = 0; i < positions.length; i++) {
let position = positions[i];
let localp = Cesium.Matrix4.multiplyByPoint(
this.localMatrix,
position.clone(),
new Cesium.Cartesian3()
)
arr.push([localp.x, localp.y]);
}
return arr;
}
/**
* 飞到
*/
async flyTo() {
setActiveViewer(0)
closeRotateAround(this.sdk)
closeViewFollow(this.sdk)
if (this.options.customView && this.options.customView.relativePosition && this.options.customView.orientation) {
let orientation = {
heading: Cesium.Math.toRadians(this.options.customView.orientation.heading || 0.0),
pitch: Cesium.Math.toRadians(this.options.customView.orientation.pitch || -60.0),
roll: Cesium.Math.toRadians(this.options.customView.orientation.roll || 0.0)
}
let lng = this.options.customView.relativePosition.lng
let lat = this.options.customView.relativePosition.lat
let alt = this.options.customView.relativePosition.alt
let destination = Cesium.Cartesian3.fromDegrees(lng, lat, alt)
let position = { lng: 0, lat: 0 }
if (this.options.position) {
position = { ...this.options.position }
}
else if (this.options.positions) {
position = { ...this.options.positions[0] }
}
else if (this.options.line && this.options.line.positions) {
position = { ...this.options.line.positions[0] }
}
else if (this.options.center) {
position = { ...this.options.center }
}
else if (this.options.start) {
position = { ...this.options.start }
}
else {
if (this.options.hasOwnProperty('lng')) {
position.lng = this.options.lng
}
if (this.options.hasOwnProperty('lat')) {
position.lat = this.options.lat
}
if (this.options.hasOwnProperty('alt')) {
position.alt = this.options.alt
}
}
// 如果没有高度值,则获取紧贴高度计算
if (!position.hasOwnProperty('alt')) {
position.alt = await this.getClampToHeight(position)
}
lng = this.options.customView.relativePosition.lng + position.lng
lat = this.options.customView.relativePosition.lat + position.lat
alt = this.options.customView.relativePosition.alt + position.alt
destination = Cesium.Cartesian3.fromDegrees(lng, lat, alt)
this.sdk.viewer.camera.flyTo({
destination: destination,
orientation: orientation
})
}
else {
let positionArray = []
for (let i = 0; i < this.options.positions.length; i++) {
let a = Cesium.Cartesian3.fromDegrees(this.options.positions[i].lng, this.options.positions[i].lat, this.center84.alt)
positionArray.push(a.x, a.y, a.z)
}
let BoundingSphere = Cesium.BoundingSphere.fromVertices(positionArray)
this.sdk.viewer.camera.flyToBoundingSphere(BoundingSphere, {
offset: {
heading: Cesium.Math.toRadians(0.0),
pitch: Cesium.Math.toRadians(-90.0),
roll: Cesium.Math.toRadians(0.0)
}
})
}
}
async edit(state) {
if (state) {
this.originalOptions = this.deepCopyObj(this.options)
this._DialogObject = await new Dialog(this.sdk.viewer._container, {
title: '压平面属性', left: '180px', top: '100px',
removeCallBack: () => {
this.Dialog.removeCallBack && this.Dialog.removeCallBack()
},
closeCallBack: () => {
this.reset()
this.Dialog.closeCallBack && this.Dialog.closeCallBack()
}
})
await this._DialogObject.init()
// 内容部分
let contentElm = document.createElement('div');
contentElm.innerHTML = `
<span class="custom-divider"></span>
<div class="div-item">
<div class="row">
<div class="col">
<span class="label" style="width: 56px;flex: 0 0 56px;">名称</span>
<input class="input input-name">
</div>
</div>
<div class="row">
<div class="col">
<span class="label" style="width: 56px;flex: 0 0 56px;">压平高度</span>
<div class="input-number input-number-unit-1">
<input class="input flat-height" type="number" title="" min="-9999999" max="999999999">
<span class="unit">m</span>
<span class="arrow"></span>
</div>
</div>
</div>
</div>
`
this._DialogObject.contentAppChild(contentElm)
let name_elm = contentElm.getElementsByClassName('input-name')[0]
name_elm.value = this.options.name
name_elm.addEventListener('input', () => {
this.name = name_elm.value
})
let height_elm = contentElm.getElementsByClassName('flat-height')[0]
height_elm.value = this.options.height
height_elm.addEventListener('input', () => {
this.height = Number(height_elm.value)
this.addFlat()
})
let confirmElm = document.createElement('button');
confirmElm.className = 'btn'
confirmElm.innerHTML = '确认'
this._DialogObject.footAppChild(confirmElm)
confirmElm.addEventListener('click', () => {
if (!this.options.name) {
this.options.name = '压平面'
}
this.originalOptions = this.deepCopyObj(this.options)
this._DialogObject.close()
this.Dialog.confirmCallBack && this.Dialog.confirmCallBack(this.options)
})
// let flatElm = document.createElement('button');
// flatElm.className = 'btn'
// flatElm.innerHTML = '<svg class="icon-edit"><use xlink:href="#yj-icon-edit"></use></svg>二次编辑'
// flatElm.style.width = 'auto'
// flatElm.style.position = 'absolute'
// flatElm.style.left = '10px'
// this._DialogObject.footAppChild(flatElm)
// flatElm.addEventListener('click', () => {
// console.log('二次编辑')
// })
}
else {
if (this._DialogObject && this._DialogObject.close) {
this._DialogObject.close()
this._DialogObject = null
}
}
}
reset() {
this.options = this.deepCopyObj(this.originalOptions)
this.name = this.options.name
this.height = this.options.height
this.addFlat()
}
flatEdit(state) {
if (state) {
let positions = that.options.positions
let fromDegreesArray = []
for (let i = 0; i < positions.length; i++) {
fromDegreesArray.push(positions[i].lng, positions[i].lat, FlatList[this.tileset.id])
}
that.positions = Cesium.Cartesian3.fromDegreesArrayHeights(fromDegreesArray)
}
}
flicker() { }
}
export default Flat;

View File

@ -0,0 +1,227 @@
import Tools from "../../../Tools";
class Flat extends Tools {
/**
* @constructor
* @description 模型压平
* @param sdk
* @param {Cesium.Cesium3DTileset} tileset 三维模型
* @param {Object} options
* @param {string} attr.id id
* @param {Number} options.height 压平高度
* @param {Cesium.Cartesian3[]} attr.positions 压平面坐标
*/
constructor(sdk, tileset, options = {}) {
super(sdk)
if (!tileset) return;
this.options = { ...options }
this.options.id = options.id || this.randomString()
this.options.positions = options.positions || []
this.tileset = tileset;
this.height = options.height;
this.center = tileset.boundingSphere.center.clone();
this.matrix = Cesium.Transforms.eastNorthUpToFixedFrame(this.center.clone());
this.localMatrix = Cesium.Matrix4.inverse(this.matrix, new Cesium.Matrix4());
// 多面的坐标数组
this.regionList = [];
// 多个面坐标转为局部模型坐标
this.localPositionsArr = [];
this.addRegion()
}
/**
* 添加压平面
* @param {Object} attr 参数
* @param {Cesium.Cartesian3[]} attr.positions 压平面坐标
* @param {Number} attr.height 压平深度,当前不支持单独设置
* @param {Number} attr.id 唯一标识
*/
addRegion(attr) {
// let { positions, height, id } = attr || {};
// // this.flatHeight = height;
// if (!id) id = (new Date()).getTime() + "" + Number(Math.random() * 1000).toFixed(0);
// this.regionList.push(attr);
// for (let i = 0; i < this.regionList.length; i++) {
// let item = this.regionList[i];
// const positions = item.positions;
// let localCoor = this.cartesiansToLocal(positions);
// this.localPositionsArr.push(localCoor);
// }
let positions = this.options.positions
let fromDegreesArray = []
for (let i = 0; i < positions.length; i++) {
fromDegreesArray.push(positions[i].lng, positions[i].lat)
}
let localCoor = this.cartesiansToLocal(Cesium.Cartesian3.fromDegreesArray(fromDegreesArray));
this.localPositionsArr.push(localCoor);
const funstr = this.getIsinPolygonFun(this.localPositionsArr);
let str = ``;
for (let i = 0; i < this.localPositionsArr.length; i++) {
const coors = this.localPositionsArr[i];
const n = coors.length;
let instr = ``;
coors.forEach((coordinate, index) => {
instr += `points_${n}[${index}] = vec2(${coordinate[0]}, ${coordinate[1]});\n`;
})
str += `
${instr}
if(isPointInPolygon_${n}(position2D)){
vec4 tileset_local_position_transformed = vec4(tileset_local_position.x, tileset_local_position.y, ground_z, 1.0);
vec4 model_local_position_transformed = czm_inverseModel * u_tileset_localToWorldMatrix * tileset_local_position_transformed;
vsOutput.positionMC.xy = model_local_position_transformed.xy;
vsOutput.positionMC.z = model_local_position_transformed.z+ modelMC.z*0.002;
return;
}`;
}
this.updateShader(funstr, str);
}
/**
* 根据id删除压平的面
* @param {String} id 唯一标识
*/
removeRegionById(id) {
if (!id) return;
this.regionList = this.regionList.filter((attr) => {
return attr.id != id;
})
this.localPositionsArr = [];
for (let i = 0; i < this.regionList.length; i++) {
let item = this.regionList[i];
const positions = item.positions;
let localCoor = this.cartesiansToLocal(positions);
this.localPositionsArr.push(localCoor);
}
const funstr = this.getIsinPolygonFun(this.localPositionsArr);
let str = ``;
for (let i = 0; i < this.localPositionsArr.length; i++) {
const coors = this.localPositionsArr[i];
const n = coors.length;
let instr = ``;
coors.forEach((coordinate, index) => {
instr += `points_${n}[${index}] = vec2(${coordinate[0]}, ${coordinate[1]});\n`;
})
str += `
${instr}
if(isPointInPolygon_${n}(position2D)){
vec4 tileset_local_position_transformed = vec4(tileset_local_position.x, tileset_local_position.y, ground_z, 1.0);
vec4 model_local_position_transformed = czm_inverseModel * u_tileset_localToWorldMatrix * tileset_local_position_transformed;
vsOutput.positionMC.xy = model_local_position_transformed.xy;
vsOutput.positionMC.z = model_local_position_transformed.z+ modelMC.z*0.002;
return;
}`;
}
this.updateShader(funstr, str);
}
/**
* 销毁
*/
destroy() {
this.tileset.customShader = undefined;
}
/**
* 根据数组长度,构建 判断点是否在面内 的压平函数
*/
getIsinPolygonFun(polygons) {
let pmap = polygons.map((polygon) => polygon.length);
let uniqueArray = this.getUniqueArray(pmap);
let str = ``;
uniqueArray.forEach(length => {
str += `
vec2 points_${length}[${length}];
bool isPointInPolygon_${length}(vec2 point){
int nCross = 0; // 交点数
const int n = ${length};
for(int i = 0; i < n; i++){
vec2 p1 = points_${length}[i];
vec2 p2 = points_${length}[int(mod(float(i+1),float(n)))];
if(p1[1] == p2[1]){
continue;
}
if(point[1] < min(p1[1], p2[1])){
continue;
}
if(point[1] >= max(p1[1], p2[1])){
continue;
}
float x = p1[0] + ((point[1] - p1[1]) * (p2[0] - p1[0])) / (p2[1] - p1[1]);
if(x > point[0]){
nCross++;
}
}
return int(mod(float(nCross), float(2))) == 1;
}
`
})
return str
}
updateShader(vtx1, vtx2) {
let flatCustomShader = new Cesium.CustomShader({
uniforms: {
u_tileset_localToWorldMatrix: {
type: Cesium.UniformType.MAT4,
value: this.matrix,
},
u_tileset_worldToLocalMatrix: {
type: Cesium.UniformType.MAT4,
value: this.localMatrix,
},
u_flatHeight: {
type: Cesium.UniformType.FLOAT,
value: this.height,
},
},
vertexShaderText: `
// 所有isPointInPolygon函数
${vtx1}
void vertexMain(VertexInput vsInput, inout czm_modelVertexOutput vsOutput){
vec3 modelMC = vsInput.attributes.positionMC;
vec4 model_local_position = vec4(modelMC.x, modelMC.y, modelMC.z, 1.0);
vec4 tileset_local_position = u_tileset_worldToLocalMatrix * czm_model * model_local_position;
vec2 position2D = vec2(tileset_local_position.x,tileset_local_position.y);
float ground_z = 0.0 + u_flatHeight;
// 多个多边形区域
${vtx2}
}`,
});
this.tileset.customShader = flatCustomShader;
this.sdk.viewer.scene.requestRender();
}
// 数组去重,不能处理嵌套的数组
getUniqueArray = (arr) => {
return arr.filter(function (item, index, arr) {
//当前元素,在原始数组中的第一个索引==当前索引值,否则返回当前元素
return arr.indexOf(item, 0) === index;
});
}
// 世界坐标转数组局部坐标
cartesiansToLocal(positions) {
let arr = [];
for (let i = 0; i < positions.length; i++) {
let position = positions[i];
let localp = Cesium.Matrix4.multiplyByPoint(
this.localMatrix,
position.clone(),
new Cesium.Cartesian3()
)
arr.push([localp.x, localp.y]);
}
return arr;
}
}
export default Flat;

View File

@ -0,0 +1,8 @@
function html() {
return `
<span class="custom-divider"></span>
<div class="profile-echarts"></div>
`
}
export { html }

View File

@ -0,0 +1,637 @@
import Draw from "../../../Draw/draw";
import MouseEvent from "../../../Event";
import MouseTip from "../../../MouseTip";
import Dialog from '../../../BaseDialog';
import { html } from "./_element";
class Profile extends Draw {
/**
* @constructor 剖面分析
* @param sdk
**/
constructor(sdk, _Dialog = {}) {
window.addEventListener("resize", () => {
this.echartsObject && this.echartsObject.resize();
});
super(sdk)
this.viewer = sdk.viewer;
this.Dialog = _Dialog
YJ.Analysis.Analyses.push(this)
Profile.create(this)
}
static create(that) {
this._currentId = Cesium.createGuid()
let id = this._currentId
that.clean()
if (YJ.Measure.GetMeasureStatus()) {
console.warn('上一次测量未结束')
} else {
YJ.Measure.SetMeasureStatus(true)
that.tip = new MouseTip('左键确定,右键取消', that.sdk)
that.event = new MouseEvent(that.sdk)
that.positions = []
that.points_ids = [] //存放左键点击时临时添加的point的id
let cache_positions = []
let car = undefined
that.event.mouse_left(async (movement, cartesian) => {
try {
if (!that.entityHasCreated) {
Profile.create_polyline(that)
}
cache_positions.push(cartesian)
that.points_ids.push(that.create_point(cartesian,))
if (cache_positions.length == 2) {
that.end()
let positions = []
cache_positions.forEach((item) => {
positions.push(that.cartesian3Towgs84(item, that.viewer))
})
Profile.interPoints(that).then((points) => {
if (this._currentId && this._currentId === id) {
that._DialogObject ? Profile.initEcharts(that, points) : Profile.edit(that, points)
}
})
}
} catch (error) {
console.log(error)
}
})
that.event.mouse_right((movement, cartesian) => {
let positions = []
cache_positions = []
that.clean()
})
that.event.mouse_move((movement, cartesian) => {
that.positions = cache_positions.concat(cartesian)
that.tip.setPosition(
cartesian,
movement.endPosition.x,
movement.endPosition.y
)
})
that.event.gesture_pinck_start((movement, cartesian) => {
let startTime = new Date()
that.event.gesture_pinck_end(() => {
let endTime = new Date()
if (endTime - startTime >= 500) {
let positions = []
cache_positions = []
that.end()
}
})
})
}
}
static create_polyline(that) {
that.entityHasCreated = true
let id = that.randomString()
that.polyline = that.viewer.entities.add(
new Cesium.Entity({
id: id,
polyline: {
positions: new Cesium.CallbackProperty(() => {
return that.positions
}, false),
width: 5,
material: Cesium.Color.fromCssColorString(that.color),
clampToGround: true,
zIndex: 99999999
},
})
)
return id
}
/**
* 线段插值点
*/
static async interPoints(that) {
let viewer = that.viewer
let positions = that.positions
let positionsCartographic = []
let positions84 = [];
for (let index = 0; index < positions.length; index++) {
const element = positions[index];
let cartographic = viewer.scene.globe.ellipsoid.cartesianToCartographic(element);
positionsCartographic.push(cartographic);
let pos84 = that.cartesian3Towgs84(element, viewer)
positions84.push(pos84);
}
let positions_Inter = [];
let height = await that.getClampToHeight({ lng: positions84[0].lng, lat: positions84[0].lat });
positions_Inter.push({
position: { lng: positions84[0].lng, lat: positions84[0].lat, height: height },
distance: 0,
});
for (let i = 0; i < positionsCartographic.length - 1; i++) {
let line = turf.lineString([[positions84[i].lng, positions84[i].lat], [positions84[i + 1].lng, positions84[i + 1].lat]]);
let totalDistance = turf.length(line, { units: 'kilometers' });
const m_Cartographic0 = positionsCartographic[i];
const m_Cartographic1 = positionsCartographic[i + 1];
let a =
Math.abs(m_Cartographic0.longitude - m_Cartographic1.longitude) *
10000000;
let b =
Math.abs(m_Cartographic0.latitude - m_Cartographic1.latitude) *
10000000;
//等距采样
if (a > b) b = a;
let length = parseInt(b / 2);
if (length > 150) length = 150;
if (length < 2) length = 2;
let distance = totalDistance / (length - 1)
for (let j = 0; j < length - 1; j++) {
let start = j * distance
let stop = (j + 1) * distance
let sliced = await turf.lineSliceAlong(line, start, stop, { units: 'kilometers' });
let lng = sliced.geometry.coordinates[sliced.geometry.coordinates.length - 1][0]
let lat = sliced.geometry.coordinates[sliced.geometry.coordinates.length - 1][1]
let height = await that.getClampToHeight({ lng: lng, lat: lat });
positions_Inter.push({
position: { lng: lng, lat: lat, height: height },
distance: stop * 1000,
});
}
}
return positions_Inter
}
static async edit(that, points) {
if (that._DialogObject && that._DialogObject.close) {
that._DialogObject.close()
that._DialogObject = null
}
that._DialogObject = await new Dialog(that.sdk.viewer._container, {
title: '剖面分析', left: '180px', top: '100px',
closeCallBack: () => {
that.clean()
that.Dialog.closeCallBack && that.Dialog.closeCallBack()
},
})
await that._DialogObject.init()
that._DialogObject._element.body.className = that._DialogObject._element.body.className + ' profile'
let contentElm = document.createElement('div');
contentElm.innerHTML = html()
that._DialogObject.contentAppChild(contentElm)
let resetBtn = document.createElement('button');
resetBtn.innerHTML = '<svg class="icon-edit"><use xlink:href="#yj-icon-edit"></use></svg>重新绘制'
resetBtn.style.width = 'auto'
resetBtn.addEventListener('click', () => {
Profile.create(that)
Profile.initEcharts(that)
})
that._DialogObject.footAppChild(resetBtn)
Profile.initEcharts(that, points)
}
static initEcharts(that, points) {
let datas = [],
coords = [];
const pointsData = points;
let option
if (pointsData) {
const maxDistance = pointsData[pointsData.length - 1].distance;
let xAixMax = Math.ceil(maxDistance);
for (let index = 0; index < pointsData.length; index++) {
const element = pointsData[index];
if (element.position.height === void 0) {
continue
}
const curData = [
element.distance.toFixed(2),
element.position.height.toFixed(2),
];
datas.push(curData);
const curCoords = [element.position.lng, element.position.lat];
coords.push(curCoords);
}
const pointOption = {
show: true,
pixelSize: 10,
color: Cesium.Color.GREEN,
outlineColor: Cesium.Color.SKYBLUE,
outlineWidth: 3,
disableDepthTestDistance: Number.POSITIVE_INFINITY
};
const ele = that._DialogObject._element.content.getElementsByClassName("profile-echarts")[0];
that.echartsObject = echarts.init(ele);
option = {
tooltip: {
trigger: "axis",
textStyle: {
align: "left",
},
formatter(params) {
const xy = coords[params[0].dataIndex];
const tipData = params[0]["data"];
if (!that.tipEntity) {
that.tipEntity = that.sdk.viewer.entities.add({
position: Cesium.Cartesian3.fromDegrees(
xy[0],
xy[1],
Number(tipData[1])
),
point: pointOption,
});
} else {
that.tipEntity.position = Cesium.Cartesian3.fromDegrees(
xy[0],
xy[1],
Number(tipData[1])
);
}
return (
"距离:" +
tipData[0] +
"m<br>" +
"高度:" +
tipData[1] +
"m<br>" +
"坐标:" +
xy[0].toFixed(5) +
"" +
xy[1].toFixed(5)
);
},
},
grid: {
top: 40,
bottom: 20,
left: 55,
right: 30
},
calculable: true,
xAxis: [
{
type: "value",
max: xAixMax,
scale: true,
axisLabel: {
color: '#ffffff'
},
axisLine: {
lineStyle: {
color: "#ffffff"
}
}
},
],
yAxis: [
{
type: "value",
scale: true,
axisLabel: {
color: '#ffffff'
},
axisLine: {
lineStyle: {
color: "#ffffff"
}
}
},
],
series: [
{
name: "ProfileLine",
type: "line",
data: datas,
smooth: true,
itemStyle: {
normal: {
color: "#39FDA1",
},
},
lineStyle: {
normal: {
width: 3,
color: {
type: "linear",
x: 0,
y: 0,
x2: 1,
y2: 0,
colorStops: [
{
offset: 0,
color: "rgba(85,254,139,1)", // 0% 处的颜色
},
{
offset: 0.5,
color: "rgba(7,252,202,1)", // 100% 处的颜色
},
{
offset: 1,
color: "rgba(14,245,210,1)", // 100% 处的颜色
},
],
globalCoord: false, // 缺省为 false
},
},
},
areaStyle: {
normal: {
color: new echarts.graphic.LinearGradient(
0,
0,
0,
1,
[
{
offset: 0,
color: "rgba(102,153,255,1)",
},
{
offset: 0.8,
color: "rgba(102,153,255,0.08)",
},
{
offset: 1,
color: "rgba(9,173,208,0.15)",
},
],
false
),
shadowColor: "rgba(14,245,210,1)", //阴影颜色
shadowBlur: 20,
},
},
markPoint: {
data: [
{
type: "max",
name: "最高点",
label: {
color: '#ffffff',
}
},
{
type: "min",
name: "最低点",
label: {
color: '#ffffff',
}
},
],
},
},
],
};
}
else {
const ele = that._DialogObject._element.content.getElementsByClassName("profile-echarts")[0];
that.echartsObject = echarts.init(ele);
option = {
tooltip: {
trigger: "axis",
textStyle: {
align: "left",
}
},
grid: {
top: 40,
bottom: 20,
left: 55,
right: 30
},
calculable: true,
xAxis: [
{
type: "value",
scale: true,
axisLabel: {
color: '#ffffff'
},
axisLine: {
lineStyle: {
color: "#ffffff"
}
}
},
],
yAxis: [
{
type: "value",
scale: true,
axisLabel: {
color: '#ffffff'
},
axisLine: {
lineStyle: {
color: "#ffffff"
}
}
},
],
series: [
{
name: "ProfileLine",
type: "line",
data: [],
smooth: true,
itemStyle: {
normal: {
color: "#39FDA1",
},
},
lineStyle: {
normal: {
width: 3,
color: {
type: "linear",
x: 0,
y: 0,
x2: 1,
y2: 0,
colorStops: [
{
offset: 0,
color: "rgba(85,254,139,1)", // 0% 处的颜色
},
{
offset: 0.5,
color: "rgba(7,252,202,1)", // 100% 处的颜色
},
{
offset: 1,
color: "rgba(14,245,210,1)", // 100% 处的颜色
},
],
globalCoord: false, // 缺省为 false
},
},
},
areaStyle: {
normal: {
color: new echarts.graphic.LinearGradient(
0,
0,
0,
1,
[
{
offset: 0,
color: "rgba(102,153,255,1)",
},
{
offset: 0.8,
color: "rgba(102,153,255,0.08)",
},
{
offset: 1,
color: "rgba(9,173,208,0.15)",
},
],
false
),
shadowColor: "rgba(14,245,210,1)", //阴影颜色
shadowBlur: 20,
},
},
markPoint: {
data: [
{
type: "max",
name: "最高点",
label: {
color: '#ffffff',
}
},
{
type: "min",
name: "最低点",
label: {
color: '#ffffff',
}
},
],
},
},
],
};
}
that.echartsObject.setOption(option);
}
clean() {
this.end()
this._currentId = null
this.entityHasCreated = false
this.polyline && this.viewer.entities.remove(this.polyline)
this.tipEntity && this.viewer.entities.remove(this.tipEntity)
this.polyline = null
this.tipEntity = null
}
destroy() {
this.clean()
if (this._DialogObject && this._DialogObject.close) {
this._DialogObject.close()
this._DialogObject = null
}
}
}
// const Profile = function (viewer, callback) {
// if (!viewer) throw new Error("no viewer object!");
// if (window.profileEntities && window.profileEntities.length > 0) {
// window.profileEntities.forEach((element) => {
// window.viewer.entities.remove(element);
// });
// }
// window.profileEntities = [];
// CreatePolyline(
// viewer,
// window.profileEntities,
// { color: Cesium.Color.RED, width: 2 },
// function (e) {
// e.polyline.clampToGround = true;
// console.log(e.pottingPoint);
// let points = interPoints(viewer, e.pottingPoint, [e]);
// console.log(points);
// if (typeof callback == "function") callback(points);
// }
// );
// };
// /**
// * 线段插值点
// * @param {*} viewer
// * @param {*} positions 线段节点集合
// * @param {*} objectsToExclude 高度采集时排除的对象集合
// * @returns 经纬度点集合,包含距离值
// */
// function interPoints(viewer, positions, objectsToExclude) {
// let positionsCartographic = [];
// let terrainSamplePositions = [];
// for (let index = 0; index < positions.length; index++) {
// const element = positions[index];
// let ellipsoid = viewer.scene.globe.ellipsoid;
// let cartographic = ellipsoid.cartesianToCartographic(element);
// positionsCartographic.push(cartographic);
// }
// for (let i = 0; i < positionsCartographic.length; i++) {
// const m_Cartographic0 = positionsCartographic[i];
// const m_Cartographic1 = positionsCartographic[i + 1];
// if (m_Cartographic1) {
// let a =
// Math.abs(m_Cartographic0.longitude - m_Cartographic1.longitude) *
// 10000000;
// let b =
// Math.abs(m_Cartographic0.latitude - m_Cartographic1.latitude) *
// 10000000;
// //等距采样
// if (a > b) b = a;
// let length = parseInt(b / 2);
// if (length > 1000) length = 1000;
// if (length < 2) length = 2;
// for (let j = 0; j < length; j++) {
// terrainSamplePositions.push(
// new Cesium.Cartographic(
// Cesium.Math.lerp(
// m_Cartographic0.longitude,
// m_Cartographic1.longitude,
// j / (length - 1)
// ),
// Cesium.Math.lerp(
// m_Cartographic0.latitude,
// m_Cartographic1.latitude,
// j / (length - 1)
// )
// )
// );
// }
// terrainSamplePositions.pop();
// } else {
// terrainSamplePositions.push(m_Cartographic0);
// }
// }
// let positions_Inter = [];
// let distance = 0;
// for (let n = 0; n < terrainSamplePositions.length; n++) {
// //地理坐标(弧度)转经纬度坐标
// let curCartographic = terrainSamplePositions[n];
// let height = viewer.scene.sampleHeight(curCartographic, objectsToExclude);
// const lon = (curCartographic.longitude / Math.PI) * 180;
// const lat = (curCartographic.latitude / Math.PI) * 180;
// let point = Cesium.Cartesian3.fromDegrees(lon, lat, height);
// let preCartographic = terrainSamplePositions[n - 1];
// if (preCartographic) {
// const lon1 = (preCartographic.longitude / Math.PI) * 180;
// const lat1 = (preCartographic.latitude / Math.PI) * 180;
// let point1 = Cesium.Cartesian3.fromDegrees(lon1, lat1, height);
// let curDis = Cesium.Cartesian3.distance(point1, point);
// distance += curDis;
// }
// positions_Inter.push({
// position: { lon: lon, lat: lat, height: height },
// distance: distance,
// });
// }
// return positions_Inter;
// }
export default Profile;

View File

@ -0,0 +1,145 @@
import Tools from "../../../Tools";
class Section extends Tools {
/**
* @constructor 剖切
* @param sdk
* @param tiles3d {object} 3dtiles对象
* @param {Array.<object>} options.positions 经纬度[{lon,lat,alt},...]
* @param options.regionsType=false 裁剪类型 false:裁剪内部true:裁剪外部
* **/
constructor(sdk, tiles3d, options = {}) {
super(sdk, options)
this.viewer = sdk.viewer
this.tiles3d = tiles3d
this.options = { ...options }
this.options.regionsType = this.options.regionsType || false
// YJ.Analysis.Analyses.push(this)
this.Planes = []
Section.start(this)
}
get regionsType() {
return this.options.regionsType
}
set regionsType(v) {
this.options.regionsType = v
if (this.Planes.length > 0) {
this.Planes = []
Section.planeCollection(this)
}
}
static start(that) {
let positions = that.options.positions || []
if(!that.isConvex(positions)) {
window.ELEMENT && window.ELEMENT.Message({
message: '不支持凹多边形',
type: 'warning',
duration: 1500
});
console.log('不支持凹多边形')
return
}
that.inverseTransform = getInverseTransform(that.tiles3d)
that.Planes = []
let array = []
if (positions.length > 0) {
for (let i = 0; i < positions.length; i++) {
array.push([positions[i].lng, positions[i].lat])
}
array.push([positions[0].lng, positions[0].lat])
that.isClockwise = turf.booleanClockwise(turf.lineString(array));
}
Section.planeCollection(that)
function getInverseTransform(tileSet) {
let transform
const tmp = tileSet.root.transform
if ((tmp && tmp.equals(Cesium.Matrix4.IDENTITY)) || !tmp) {
transform = Cesium.Transforms.eastNorthUpToFixedFrame(tileSet.boundingSphere.center)
} else {
transform = Cesium.Matrix4.fromArray(tileSet.root.transform)
}
return Cesium.Matrix4.inverseTransformation(transform, new Cesium.Matrix4())
}
}
static planeCollection(that) {
let positions = that.options.positions || []
if (that.regionsType == that.isClockwise) {
for (let i = 0; i < positions.length; i++) {
if (i === (positions.length - 1)) {
that.Planes.push(createPlane(positions[i], positions[0], that.inverseTransform))
} else {
that.Planes.push(createPlane(positions[i], positions[i + 1], that.inverseTransform))
}
}
}
else {
for (let i = positions.length - 1; i >= 0; i--) {
if (i === 0) {
that.Planes.push(createPlane(positions[i], positions[positions.length - 1], that.inverseTransform))
} else {
that.Planes.push(createPlane(positions[i], positions[i - 1], that.inverseTransform))
}
}
}
if(that.tiles3d.clippingPlanes) {
that.tiles3d.clippingPlanes.removeAll()
for(let i=0;i<that.Planes.length;i++) {
that.tiles3d.clippingPlanes.add(that.Planes[i])
}
that.tiles3d.clippingPlanes.enabled = true
}
else {
const PlaneCollection = new Cesium.ClippingPlaneCollection({
planes: that.Planes,
enabled: true,
unionClippingRegions: that.regionsType,
edgeColor: Cesium.Color.WHITE,
edgeWidth: 1,
})
that.tiles3d.clippingPlanes = PlaneCollection
}
function createPlane(p1, p2, inverseTransform) {
// 将仅包含经纬度信息的p1,p2转换为相应坐标系的cartesian3对象
const p1C3 = getOriginCoordinateSystemPoint(p1, inverseTransform)
const p2C3 = getOriginCoordinateSystemPoint(p2, inverseTransform)
// 定义一个垂直向上的向量up
const up = new Cesium.Cartesian3(0, 0, 10)
// right 实际上就是由p1指向p2的向量
const right = Cesium.Cartesian3.subtract(p2C3, p1C3, new Cesium.Cartesian3())
// 计算normal right叉乘up得到平面法向量这个法向量指向right的右侧
let normal = Cesium.Cartesian3.cross(right, up, new Cesium.Cartesian3())
normal = Cesium.Cartesian3.normalize(normal, normal)
// 由于已经获得了法向量和过平面的一点因此可以直接构造Plane,并进一步构造ClippingPlane
const planeTmp = Cesium.Plane.fromPointNormal(p1C3, normal)
return Cesium.ClippingPlane.fromPlane(planeTmp)
}
function getOriginCoordinateSystemPoint(point, inverseTransform) {
const val = Cesium.Cartesian3.fromDegrees(point.lng, point.lat)
return Cesium.Matrix4.multiplyByPoint(
inverseTransform, val, new Cesium.Cartesian3(0, 0, 0))
}
}
destroy() {
this.Planes = []
// this.tiles3d.clippingPlanes = new Cesium.ClippingPlaneCollection()
if(this.tiles3d.clippingPlanes) {
this.tiles3d.clippingPlanes.enabled = false
this.tiles3d.clippingPlanes.removeAll()
}
}
}
export default Section;

View File

@ -0,0 +1,517 @@
import Tools from "../../../Tools";
import DrawPolygon from "../../../Draw/drawPolygon"
import MouseEvent from '../../../Event/index'
class SlopeAspect extends Tools {
/**
* @constructor 坡度坡向分析
* @param sdk
* **/
constructor(sdk) {
super(sdk)
this.viewer = sdk.viewer;
let terrainAvailability = this.viewer.terrainProvider.availability;
if (!terrainAvailability) {
this.error = '未加载地形数据!'
window.ELEMENT && window.ELEMENT.Message({
message: '未加载地形数据!',
type: 'warning',
duration: 1500
});
return
}
this.event
this.result = []; //存储创建的坡度分析结果primitive集合
this.handler = undefined;
this.toolTip = "";
YJ.Analysis.Analyses.push(this)
this.Draw = new DrawPolygon(this.sdk)
// this.createNew4Distance()
this.createNew4Num(50)
}
//等距离切分网格
createNew4Distance(distance) {
distance = distance || 0.1; //默认0.1km精度
let width = distance * 200 > 35 ? 35 : distance * 200;
this.arrowWidth = width < 15 ? 15 : width;
const $this = this;
const viewer = this.viewer;
this.Draw.start((e, positions) => {
if (!positions || positions.length <= 2) {
window.ELEMENT && window.ELEMENT.Message({
message: '至少拥有三个坐标位置!',
type: 'warning',
duration: 1500
});
return
}
let boundary = [];
let minX = 10000,
minY = 10000,
maxX = -10000,
maxY = -1000;
for (let index = 0; index < positions.length; index++) {
const element = positions[index];
const x = element.lng;
const y = element.lat;
boundary.push([x, y]);
minX = x < minX ? x : minX;
minY = y < minY ? y : minY;
maxX = x > maxX ? x : maxX;
maxY = y > maxY ? y : maxY;
}
boundary.push(boundary[0]);
let bbox = [minX, minY, maxX, maxY];
let mask = turf.polygon([boundary]);
let gridSquare = turf.squareGrid(bbox, distance, { mask: mask });
this.createEllipse(gridSquare);
})
}
// 等分切分网格切分成一个num*num的网格
createNew4Num(n) {
let num = n
this.Draw.start((e, positions) => {
if (!positions || positions.length <= 2) {
console.warn('至少拥有三个坐标位置!')
return
}
let boundary = [];
let minX = 10000,
minY = 10000,
maxX = -10000,
maxY = -1000;
for (let index = 0; index < positions.length; index++) {
const element = positions[index];
const x = element.lng;
const y = element.lat;
boundary.push([x, y]);
minX = x < minX ? x : minX;
minY = y < minY ? y : minY;
maxX = x > maxX ? x : maxX;
maxY = y > maxY ? y : maxY;
}
boundary.push(boundary[0]);
let bbox = [minX, minY, maxX, maxY];
let a = maxX - minX;
let b = maxY - minY;
b = b > a ? b : a;
// 根据面积修改网格数
let mask = turf.polygon([boundary]);
let area = turf.area(mask);
if (area > 5000000000000) {
num = num - 25;
}
else if (area > 1000000000000) {
num = num - 20;
}
else if (area > 500000000000) {
num = num - 15;
}
else if (area > 100000000000) {
num = num - 10;
}
else if (area > 60000000000) {
num = num - 5;
}
const step = b / num;
let width = step * 2000 > 35 ? 35 : step * 2000;
this.arrowWidth = width < 15 ? 15 : width;
let gridSquare = turf.squareGrid(bbox, step, {
units: "degrees",
mask: mask,
});
this.createEllipse(gridSquare);
})
// CreatePolygonOnGround(
// viewer,
// [],
// {
// color: Cesium.Color.RED.withAlpha(0.1),
// outlineColor: Cesium.Color.YELLOW,
// outlineWidth: 2,
// },
// function (polygon) {
// let degrees = $this.Cartesian3ListToWGS84(polygon.pottingPoint);
// viewer.entities.remove(polygon);
// let boundary = [];
// let minX = 10000,
// minY = 10000,
// maxX = -10000,
// maxY = -1000;
// for (let index = 0; index < degrees.length; index++) {
// const element = degrees[index];
// const x = element.lng;
// const y = element.lat;
// boundary.push([x, y]);
// minX = x < minX ? x : minX;
// minY = y < minY ? y : minY;
// maxX = x > maxX ? x : maxX;
// maxY = y > maxY ? y : maxY;
// }
// boundary.push(boundary[0]);
// let bbox = [minX, minY, maxX, maxY];
// let a = maxX - minX;
// let b = maxY - minY;
// b = b > a ? b : a;
// const step = b / num;
// let width = step * 2000 > 35 ? 35 : step * 2000;
// this.arrowWidth = width < 15 ? 15 : width;
// let mask = turf.polygon([boundary]);
// let gridSquare = turf.squareGrid(bbox, step, {
// units: "degrees",
// mask: mask,
// });
// this.createEllipse(gridSquare);
// }
// );
}
createEllipse(gridSquare) {
let boxResults = [];
for (let index = 0; index < gridSquare.features.length; index++) {
const feature = gridSquare.features[index];
const coordinates = feature.geometry.coordinates[0];
const centerdegree = [
(coordinates[0][0] + coordinates[2][0]) / 2,
(coordinates[0][1] + coordinates[2][1]) / 2,
];
let centerCartographic = Cesium.Cartographic.fromDegrees(
centerdegree[0],
centerdegree[1]
);
boxResults.push(centerCartographic);
for (let i = 0; i < coordinates.length; i++) {
const coord = coordinates[i];
let cartographic = Cesium.Cartographic.fromDegrees(coord[0], coord[1]);
boxResults.push(cartographic);
const coord1 = coordinates[i + 1];
if (coord1) {
let newCoord = [
(coord[0] + coord1[0]) / 2,
(coord[1] + coord1[1]) / 2,
];
let newCartographic = Cesium.Cartographic.fromDegrees(
newCoord[0],
newCoord[1]
);
boxResults.push(newCartographic);
}
}
}
let _this = this
// 点位过多,分为三份计算
let num = (Math.floor(boxResults.length / 3) + '')
num = Number(num.substring(0, num.length - 1))*10
let i=0
let points = boxResults.slice(i * num, (i + 1) * num)
if (points.length > 0) {
sampleTerrainMostDetailed(points)
}
function sampleTerrainMostDetailed(ps) {
Cesium.sampleTerrainMostDetailed(
_this.viewer.scene.terrainProvider,
ps
).then((updatePositions) => {
i++
let points = boxResults.slice(i * num, (i + 1) * num)
if (points.length > 0) {
sampleTerrainMostDetailed(points)
}
let arrr = [];
let ellipseResults = updatePositions.reduce(function (
pre,
item,
index,
updatePositions
) {
var begin = index * 10;
var end = begin + 10;
var res = updatePositions.slice(begin, end);
if (res.length != 0) {
arrr[index] = res;
}
return arrr;
},
[]);
_this.calculateSlope(ellipseResults);
});
}
}
createPolygonInsrance(points, color, curSlope) {
let positions = [];
for (let index = 1; index < points.length - 1; index++) {
const element = points[index];
positions.push(Cesium.Cartographic.toCartesian(element));
}
let polygon = new Cesium.PolygonGeometry({
polygonHierarchy: new Cesium.PolygonHierarchy(positions),
});
let polygonInstance = new Cesium.GeometryInstance({
id: {
type: "SlopeAspect",
value: curSlope
},
geometry: polygon,
attributes: {
color: Cesium.ColorGeometryInstanceAttribute.fromColor(
Cesium.Color.fromCssColorString(color)
),
show: new Cesium.ShowGeometryInstanceAttribute(true), //显示或者隐藏
},
});
return polygonInstance;
}
createArrowInstance(
targetPoint,
center,
diagonalPoint,
heightDifference,
curSlope
) {
let cartographic_0 = new Cesium.Cartographic(
(targetPoint.longitude + center.longitude) / 2,
(targetPoint.latitude + center.latitude) / 2,
(targetPoint.height + center.height) / 2
);
let cartographic_1 = new Cesium.Cartographic(
(diagonalPoint.longitude + center.longitude) / 2,
(diagonalPoint.latitude + center.latitude) / 2,
(diagonalPoint.height + center.height) / 2
);
//偏移的
let positions1 =
heightDifference > 0
? [
Cesium.Cartographic.toCartesian(cartographic_0),
Cesium.Cartographic.toCartesian(cartographic_1),
]
: [
Cesium.Cartographic.toCartesian(cartographic_1),
Cesium.Cartographic.toCartesian(cartographic_0),
];
//箭头线
const instance = new Cesium.GeometryInstance({
id: {
type: "SlopeAspect",
value: curSlope,
},
geometry: new Cesium.GroundPolylineGeometry({
positions: positions1,
width: this.arrowWidth,
}),
attributes: {
color: Cesium.ColorGeometryInstanceAttribute.fromColor(
Cesium.Color.BLUE.withAlpha(0.6)
),
show: new Cesium.ShowGeometryInstanceAttribute(true), //显示或者隐藏
},
});
return instance;
}
calculateSlope(ellipseResults) {
let instances = [];
let polygonInstance = [];
for (let index = 0; index < ellipseResults.length; index++) {
const ellipse = ellipseResults[index];
const center = ellipse[0];
let heightDifference = 0;
let maxIndex = 0;
for (let i = 1; i < ellipse.length - 1; i++) {
const point = ellipse[i];
let curHD = point.height - center.height;
if (Math.abs(curHD) > heightDifference) {
heightDifference = curHD;
maxIndex = i;
}
}
let pos0 = new Cesium.Cartographic(center.longitude, center.latitude, 0);
let pos1 = new Cesium.Cartographic(
ellipse[maxIndex].longitude,
ellipse[maxIndex].latitude,
0
);
let distance = Cesium.Cartesian3.distance(
Cesium.Cartographic.toCartesian(pos0),
Cesium.Cartographic.toCartesian(pos1)
);
let curSlope = Math.abs(heightDifference / distance); //坡度的tan值
let curColor = this.calculateSlopeColor(curSlope, 0.4);
const curPolygonInstance = this.createPolygonInsrance(ellipse, curColor, curSlope);
polygonInstance.push(curPolygonInstance);
let diagonalPoint =
maxIndex > 4 ? ellipse[maxIndex - 4] : ellipse[maxIndex + 4]; //对角点
let targetPoint = ellipse[maxIndex];
const arrowInstance = this.createArrowInstance(
targetPoint,
center,
diagonalPoint,
heightDifference,
curSlope
);
instances.push(arrowInstance);
}
const mapPrimitive = this.viewer.scene.primitives.add(
new Cesium.GroundPrimitive({
geometryInstances: polygonInstance,
appearance: new Cesium.PerInstanceColorAppearance({
translucent: true, //false时透明度无效
closed: false,
}),
})
);
const arrowPrimitive = this.viewer.scene.primitives.add(
new Cesium.GroundPolylinePrimitive({
geometryInstances: instances,
appearance: new Cesium.PolylineMaterialAppearance({
material: new Cesium.Material({
fabric: {
type: "PolylineArrow",
uniforms: {
color: new Cesium.Color(1.0, 1.0, 0.0, 0.8),
},
},
}),
}),
})
);
this.result.push(arrowPrimitive, mapPrimitive);
this.event = new MouseEvent(this.sdk)
let mouseEvent = (movement, cartesian) => {
// console.log(movement, cartesian)
let infoBox = document.getElementById('SlopeAspect-box')
if (!infoBox) {
infoBox = document.createElement('div')
infoBox.id = 'SlopeAspect-box'
infoBox.style.pointerEvents = 'none'
infoBox.style.display = 'none'
infoBox.style.position = 'absolute'
infoBox.style.background = '#333333'
infoBox.style.color = '#fff'
infoBox.style.color = '#fff'
infoBox.style.padding = '5px'
infoBox.style.fontSize = '12px'
infoBox.style.borderRadius = '5px'
infoBox.style.transform = 'translate(-50%, -10px)'
infoBox.innerHTML = `
<div class="value">坡度:</div>
<span style="
position: absolute;
border: 4px solid;
border-color: #fff0 #fff0 #333333 #333333;
transform: rotate(-45deg);
left: calc(50% - 5px);
"></span>
`
document.body.appendChild(infoBox)
}
let vlaElm = infoBox.getElementsByClassName('value')[0]
let position = { ...movement.position }
let pickedObject = this.sdk.viewer.scene.pick(position);
if (pickedObject && pickedObject.id && pickedObject.id.type && pickedObject.id.type === "SlopeAspect") {
let top = 0
let left = 0
if (this.sdk.viewer && this.sdk.viewer._element) {
let element = this.sdk.viewer._element.getElementsByClassName('cesium-widget')[0].getElementsByTagName('canvas')[0]
top = element.getBoundingClientRect().top + window.scrollY
left = element.getBoundingClientRect().left + window.scrollX
}
infoBox.style.display = 'block'
infoBox.style.left = position.x + 2 + left + 'px'
infoBox.style.top = position.y - 20 + top + 'px'
vlaElm.innerHTML = '坡度:' + Number(Cesium.Math.toDegrees(pickedObject.id.value || 0).toFixed(2)) + '°'
}
else {
infoBox.style.display = 'none'
}
}
this.event.mouse_move((movement, cartesian) => {
let newMovement = {
position: { ...movement.endPosition }
}
mouseEvent(newMovement, cartesian)
})
this.event.mouse_left(mouseEvent)
this._camera = {
position: this.sdk.viewer.camera.position,
heading: this.sdk.viewer.camera.heading,
pitch: this.sdk.viewer.camera.pitch,
roll: this.sdk.viewer.camera.roll
}
this.sdk.viewer.scene.preRender.addEventListener(this._watchEvent, this)
// this.sdk.viewer.clock.onTick.addEventListener(() => {
// console.log(111111)
// let infoBox = document.getElementById('SlopeAspect-box')
// if(infoBox) {
// infoBox.style.display = 'none'
// }
// })
}
_watchEvent() {
if (
this._camera.position.x.toFixed(8) !== this.sdk.viewer.camera.position.x.toFixed(8) ||
this._camera.position.y.toFixed(8) !== this.sdk.viewer.camera.position.y.toFixed(8) ||
this._camera.position.z.toFixed(8) !== this.sdk.viewer.camera.position.z.toFixed(8) ||
this._camera.heading.toFixed(8) !== this.sdk.viewer.camera.heading.toFixed(8) ||
this._camera.pitch.toFixed(8) !== this.sdk.viewer.camera.pitch.toFixed(8) ||
this._camera.roll.toFixed(8) !== this.sdk.viewer.camera.roll.toFixed(8)
) {
let infoBox = document.getElementById('SlopeAspect-box')
if (infoBox) {
infoBox.style.display = 'none'
}
}
this._camera = {
position: this.sdk.viewer.camera.position,
heading: this.sdk.viewer.camera.heading,
pitch: this.sdk.viewer.camera.pitch,
roll: this.sdk.viewer.camera.roll
}
}
//根据坡度值赋值颜色
calculateSlopeColor(value, alpha) {
// 0°0.5°为平原0.00872686779075879,rgb(85,182,43)
// 0.5°2°为微斜坡0.03492076949174773,rgb(135,211,43)
// 2°5°为缓斜坡0.08748866352592401,rgb(204,244,44)
// 5°15°为斜坡0.2679491924311227,rgb(245,233,44)
// 15°35°为陡坡0.7002075382097097,rgb(255,138,43)
// 35°55°为峭坡1.4281480067421144,rgb(255,84,43)
// 55°90°为垂直壁,rgb(255,32,43)
if (value < 0.00872686779075879) {
return "rgba(85,182,43," + alpha + ")";
} else if (value < 0.03492076949174773) {
return "rgba(135,211,43," + alpha + ")";
} else if (value < 0.08748866352592401) {
return "rgba(204,244,44," + alpha + ")";
} else if (value < 0.2679491924311227) {
return "rgba(245,233,44," + alpha + ")";
} else if (value < 0.7002075382097097) {
return "rgba(255,138,43," + alpha + ")";
} else if (value < 1.4281480067421144) {
return "rgba(255,84,43," + alpha + ")";
} else {
return "rgba(255,32,43," + alpha + ")";
}
}
destroy() {
this.result && this.result.forEach((element) => {
this.viewer.scene.primitives.remove(element);
});
this.result = [];
this.sdk.viewer.scene.preRender.removeEventListener(this._watchEvent, this)
}
}
export default SlopeAspect;

View File

@ -0,0 +1,92 @@
function html() {
return `
<span class="custom-divider"></span>
<div class="div-item">
<div class="row">
<div class="col">
<span class="label">水量</span>
<div class="input-number input-number-unit-3">
<input class="input" type="number" title="" name="waterVolume">
<span class="unit">m³</span>
<span class="arrow"></span>
</div>
</div>
<div class="col">
<span class="label">最小水位</span>
<div class="input-number input-number-unit-3">
<input class="input" type="number" title="" name="minWaterLevel">
<span class="unit">m</span>
<span class="arrow"></span>
</div>
</div>
</div>
<div class="row">
<div class="col">
<span class="label">水面面积</span>
<div class="input-number input-number-unit-3">
<input class="input area" type="number" readonly="readonly" type="text">
<span class="unit">㎡</span>
<span class="arrow"></span>
</div>
</div>
<div class="col">
<span class="label">最大水位</span>
<div class="input-number input-number-unit-3">
<input class="input" type="number" title="" name="maxWaterLevel">
<span class="unit">m</span>
<span class="arrow"></span>
</div>
</div>
</div>
</div>
<span class="custom-divider"></span>
<div class="div-item">
<div class="row subtitle-box">
<span class="subtitle">上升速度</span>
</div>
<div class="row">
<div class="col">
<input type="range" max="50" min="0" step="0.01" name="risingSpeed">
<div class="input-number input-number-unit-3" style="flex: 0 0 110px;margin-left: 10px;">
<input class="input" type="number" title="" name="risingSpeed">
<span class="unit">m/s</span>
<span class="arrow"></span>
</div>
</div>
</div>
</div>
<span class="custom-divider"></span>
<div class="div-item">
<div class="row">
<div class="col operate-btn-box">
<button class="draw"><svg class="icon-draw"><use xlink:href="#yj-icon-draw"></use></svg>绘制范围</button>
<button class="flyto"><svg class="icon-positions"><use xlink:href="#yj-icon-positions"></use></svg>定位</button>
<button class="reset"><svg class="icon-reset"><use xlink:href="#yj-icon-reset"></use></svg>重置</button>
<button class="analog"><svg class="icon-play"><use xlink:href="#yj-icon-play"></use></svg>开始模拟</button>
<button class="pause" style="margin-right: 0px;"><svg class="icon-pause"><use xlink:href="#yj-icon-pause"></use></svg>暂停</button>
<button class="start" style="display: none;margin-right: 0px;"><svg class="icon-play"><use xlink:href="#yj-icon-play"></use></svg>播放</button>
</div>
</div>
</div>
<div class="table">
<div class="table-head">
<div class="tr">
<div class="th">序号</div>
<div class="th">经度</div>
<div class="th">纬度</div>
<div class="th">高程</div>
</div>
</div>
<div class="table-body">
</div>
<div class="table-empty">
<div class="empty-img"></div>
<p>暂无数据</p>
</div>
</div>
<span class="custom-divider" style="margin-top: 20px;"></span>
`
}
export { html }

View File

@ -0,0 +1,484 @@
import Dialog from '../../../BaseDialog';
import { html } from "./_element";
import DrawPolygon from "../../../Draw/drawPolygon"
import Tools from "../../../Tools";
import { closeRotateAround, closeViewFollow} from '../../../Global/global'
class Submerge extends Tools {
/**
* @constructor
* @param sdk
* @description 淹没效果
* */
constructor(sdk, options = {}, _Dialog = {}) {
super(sdk, options);
this.sdk = sdk
this.options = {}
this.options.name = options.name
this.options.risingSpeed = 1
this.options.minWaterLevel = 0
this.options.maxWaterLevel = 0
this.options.waterVolume = 0
this.currentWaterLaver
this.color = '#00d9ff66'
this.Dialog = _Dialog
this.Draw = new DrawPolygon(this.sdk)
this.positions
this.status = true
this.area = 0
this._elms = {};
YJ.Analysis.Analyses.push(this)
Submerge.EditBox(this)
// Submerge.create(this)
}
static create(that) {
that.Draw.start((a, positions) => {
if (!positions || positions.length < 3) {
let _error = '至少需要三个坐标!'
console.warn(_error)
window.ELEMENT &&
window.ELEMENT.Message({
message: _error,
type: 'warning',
duration: 1500
})
return
}
that.destroy()
if (!positions || positions.length == 0) {
that.positions = []
that._positions = []
that.options.minWaterLevel = 0
that.options.maxWaterLevel = 0
that.options.waterVolume = 0
that.area = 0
return
}
let fromDegreesArray = []
that.positions = positions
that._positions = positions
that.options.minWaterLevel = positions[0].alt
for (let i = 0; i < positions.length; i++) {
if (that.options.minWaterLevel > positions[i].alt) {
that.options.minWaterLevel = positions[i].alt
}
fromDegreesArray.push(positions[i].lng, positions[i].lat)
}
// for (let i = 0; i < positions.length; i++) {
// fromDegreesArray.push(positions[i].lng, positions[i].lat, that.options.minWaterLevel)
// }
let pos = Cesium.Cartesian3.fromDegreesArray(fromDegreesArray)
that.currentWaterLaver = that.options.minWaterLevel
that.entity = that.sdk.viewer.entities.add({
polygon: {
hierarchy: new Cesium.PolygonHierarchy(pos),
height: new Cesium.CallbackProperty(function () {
return that.options.minWaterLevel
}, false),
extrudedHeight: new Cesium.CallbackProperty(function () {
return that.currentWaterLaver
}, false),
material: Cesium.Color.fromCssColorString(that.color),
},
})
that.area = that.computeArea(positions)
if (that.TweenAnimate) {
TWEEN.remove(that.TweenAnimate)
that.TweenAnimate = null
}
let contentElm = that._DialogObject._element.body
let pauseBtn = contentElm.getElementsByClassName('pause')[0];
let startBtn = contentElm.getElementsByClassName('start')[0];
startBtn.style.display = 'flex'
pauseBtn.style.display = 'none'
// that.move()
// Submerge.EditBox(that)
})
}
static async EditBox(that) {
if (that._DialogObject && that._DialogObject.close) {
that._DialogObject.close()
that._DialogObject = null
}
that._DialogObject = await new Dialog(that.sdk.viewer._container, {
title: '淹没分析', left: '180px', top: '100px',
closeCallBack: () => {
that.destroy()
that.Dialog.closeCallBack && that.Dialog.closeCallBack()
},
})
await that._DialogObject.init()
that._DialogObject._element.body.className = that._DialogObject._element.body.className + ' submerge'
let contentElm = document.createElement('div');
contentElm.innerHTML = html()
that._DialogObject.contentAppChild(contentElm)
let stopBtn = document.createElement('button');
stopBtn.className = 'el-button'
stopBtn.innerHTML = '暂停'
stopBtn.style.width = '80px'
let drawBtn = contentElm.getElementsByClassName('draw')[0]
drawBtn.addEventListener('click', () => {
Submerge.create(that)
})
let analogBtn = contentElm.getElementsByClassName('analog')[0];
analogBtn.addEventListener('click', () => {
that.move()
})
let flytoBtn = contentElm.getElementsByClassName('flyto')[0];
flytoBtn.addEventListener('click', () => {
that.flyTo()
})
let resetBtn = contentElm.getElementsByClassName('reset')[0];
resetBtn.addEventListener('click', () => {
that.restart()
})
let pauseBtn = contentElm.getElementsByClassName('pause')[0];
let startBtn = contentElm.getElementsByClassName('start')[0];
pauseBtn.addEventListener('click', () => {
that.pause()
pauseBtn.style.display = 'none'
startBtn.style.display = 'flex'
})
startBtn.addEventListener('click', () => {
that.start()
startBtn.style.display = 'none'
pauseBtn.style.display = 'flex'
})
// that._DialogObject.footAppChild(stopBtn)
// that._DialogObject.footAppChild(resetBtn)
// that._DialogObject.footAppChild(flytoBtn)
// that._DialogObject.footAppChild(analogBtn)
// that._DialogObject.footAppChild(drawBtn)
// 速度
let e_risingSpeed = contentElm.querySelectorAll("input[name='risingSpeed']")
e_risingSpeed[0].value = that.options.risingSpeed
e_risingSpeed[1].value = that.options.risingSpeed
e_risingSpeed[0].addEventListener('input', e => {
that.options.risingSpeed = Number(e.target.value);
});
e_risingSpeed[1].addEventListener('input', e => {
if (e.data != '.') {
let value = Number(e.target.value)
let max = Number(e_risingSpeed[0].max)
let min = Number(e_risingSpeed[0].min)
if (value > max) {
that.options.risingSpeed = max;
}
else if (value < min) {
that.options.risingSpeed = min;
}
else {
that.options.risingSpeed = Math.floor(value * 100) / 100;
}
}
});
Object.defineProperty(that.options, 'risingSpeed', {
get() {
return e_risingSpeed[0].value
},
set(value) {
e_risingSpeed[0].value = value
e_risingSpeed[1].value = value
}
})
that.waterLevel = that.options.maxWaterLevel - that.options.minWaterLevel
// 最低水位
let e_minWaterLevel = contentElm.querySelector("input[name='minWaterLevel']")
e_minWaterLevel.value = that.options.minWaterLevel
e_minWaterLevel.addEventListener('input', e => {
if (e.data != '.') {
let value = Number(e.target.value)
if (value > 999999999) {
value = 999999999
}
if (value < 0) {
value = 0
}
that.options.minWaterLevel = Math.floor(value * 10000) / 10000;
that.options.maxWaterLevel = that.options.minWaterLevel + that.waterLevel;
}
});
Object.defineProperty(that.options, 'minWaterLevel', {
get() {
return Number(e_minWaterLevel.value)
},
set(value) {
e_minWaterLevel.value = Math.floor(Number(value) * 10000) / 10000;
}
})
// 最高水位
let e_maxWaterLevel = contentElm.querySelector("input[name='maxWaterLevel']")
e_maxWaterLevel.value = that.options.maxWaterLevel
e_maxWaterLevel.addEventListener('input', e => {
if (e.data != '.') {
let value = Number(e.target.value)
if (value > 999999999) {
value = 999999999
}
if (value < 0) {
value = 0
}
if (value < that.options.minWaterLevel) {
that.options.maxWaterLevel = that.options.minWaterLevel;
}
else {
that.options.maxWaterLevel = Math.floor(value * 10000) / 10000;
}
that.waterLevel = that.options.maxWaterLevel - that.options.minWaterLevel
that.options.waterVolume = Number((that.waterLevel * that.area).toFixed(4))
}
});
Object.defineProperty(that.options, 'maxWaterLevel', {
get() {
return Number(e_maxWaterLevel.value)
},
set(value) {
if (isNaN(value)) {
value = 0
}
e_maxWaterLevel.value = Math.floor(Number(value) * 10000) / 10000;
}
})
// 水量
let e_waterVolume = contentElm.querySelector("input[name='waterVolume']")
e_waterVolume.value = that.options.waterVolume
e_waterVolume.addEventListener('input', e => {
if (e.data != '.') {
let value = Number(e.target.value)
if (value > 99999999999999) {
value = 99999999999999
}
if (value < 0) {
value = 0
}
that.options.waterVolume = Math.floor(value * 10000) / 10000;
if (that.area) {
that.waterLevel = Number((that.options.waterVolume / that.area).toFixed(4))
that.options.maxWaterLevel = that.options.minWaterLevel + that.waterLevel
}
}
});
Object.defineProperty(that.options, 'waterVolume', {
get() {
return Number(e_waterVolume.value)
},
set(value) {
e_waterVolume.value = value
}
})
// 面积
let e_area = contentElm.getElementsByClassName('area')[0]
e_area.value = that.area
Object.defineProperty(that, 'area', {
get() {
return Number(e_area.value)
},
set(value) {
e_area.value = value
that.waterLevel = Number((that.options.waterVolume / that.area).toFixed(4))
that.options.maxWaterLevel = that.options.minWaterLevel + that.waterLevel
}
})
// 表格
let e_tableBody = contentElm.getElementsByClassName('table-body')[0]
let e_tableEmpty = contentElm.getElementsByClassName('table-empty')[0]
Object.defineProperty(that, 'positions', {
get() {
return that._positions
},
set(value) {
if (value && value.length > 0) {
e_tableEmpty.style.display = 'none'
let tr = ''
for (let i = 0; i < value.length; i++) {
tr = tr + `<div class="tr">
<div class="td">${i + 1}</div>
<div class="td">${Number(value[i].lng.toFixed(10))}</div>
<div class="td">${Number(value[i].lat.toFixed(10))}</div>
<div class="td">${Number(value[i].alt.toFixed(4))}</div>
</div>`
}
e_tableBody.innerHTML = tr
}
else {
e_tableBody.innerHTML = ''
e_tableEmpty.style.display = 'flex'
}
}
})
}
move() {
if (this.TweenAnimate) {
TWEEN.remove(this.TweenAnimate)
}
let totalTime = ((this.options.maxWaterLevel - this.options.minWaterLevel) / this.options.risingSpeed) * 1000
this.TweenAnimate = new TWEEN.Tween({ waterLevel: this.options.minWaterLevel }).to({ waterLevel: this.options.maxWaterLevel }, totalTime).delay(this.delay).easing(TWEEN.Easing.Linear.None).onUpdate(async (r, a) => {
this.currentWaterLaver = r.waterLevel
}).start()
let contentElm = this._DialogObject._element.body
let pauseBtn = contentElm.getElementsByClassName('pause')[0];
let startBtn = contentElm.getElementsByClassName('start')[0];
startBtn.style.display = 'none'
pauseBtn.style.display = 'flex'
}
restart() {
this.currentWaterLaver = this.options.minWaterLevel
let isPaused = false
if (this.TweenAnimate) {
isPaused = this.TweenAnimate._isPaused
TWEEN.remove(this.TweenAnimate)
}
let totalTime = ((this.options.maxWaterLevel - this.options.minWaterLevel) / this.options.risingSpeed) * 1000
this.TweenAnimate = new TWEEN.Tween({ waterLevel: this.options.minWaterLevel }).to({ waterLevel: this.options.maxWaterLevel }, totalTime).delay(this.delay).easing(TWEEN.Easing.Linear.None).onUpdate(async (r, a) => {
this.currentWaterLaver = r.waterLevel
}).start()
if (isPaused) {
this.pause()
}
}
start() {
if (this.TweenAnimate) {
this.TweenAnimate.resume()
}
}
pause() {
if (this.TweenAnimate) {
this.TweenAnimate.pause()
}
}
calculateVolumeHeight() {
that.options.maxWaterLevel
}
/**
* 飞到
*/
flyTo() {
if (!this.positions || this.positions.length === 0) {
return
}
closeRotateAround(this.sdk)
closeViewFollow(this.sdk)
let positionArray = []
for (let i = 0; i < this.positions.length; i++) {
let fromDegrees = Cesium.Cartesian3.fromDegrees(this.positions[i].lng, this.positions[i].lat, this.options.maxWaterLevel)
positionArray.push(fromDegrees.x, fromDegrees.y, fromDegrees.z)
}
let BoundingSphere = Cesium.BoundingSphere.fromVertices(positionArray)
this.sdk.viewer.camera.flyToBoundingSphere(BoundingSphere, {
offset: {
heading: Cesium.Math.toRadians(0.0),
pitch: Cesium.Math.toRadians(-90.0),
roll: Cesium.Math.toRadians(0.0)
}
})
}
destroy() {
if (this.TweenAnimate) {
TWEEN.remove(this.TweenAnimate)
}
this.Draw.end()
this.sdk.viewer.entities.remove(this.entity)
this.entity = null
}
static EventBinding(that, elements) {
for (let i = 0; i < elements.length; i++) {
let Event = []
let isEvent = false
let removeName = []
if (!elements[i] || !elements[i].attributes) {
continue;
}
for (let m of elements[i].attributes) {
switch (m.name) {
case '@model': {
isEvent = true
if (elements[i].type == 'checkbox') {
Event.push((e) => { that[m.value] = e.target.checked })
elements[i].checked = that[m.value]
}
else {
Event.push((e) => {
let value = e.target.value
if (e.target.type == 'number') {
value = Number(value)
}
that[m.value] = value
})
if (elements[i].nodeName == 'IMG') {
elements[i].src = that[m.value]
}
else {
elements[i].value = that[m.value]
}
}
if (that._elms[m.value]) {
that._elms[m.value].push(elements[i])
}
else {
that._elms[m.value] = [elements[i]]
}
removeName.push(m.name)
break;
}
case '@click': {
elements[i].addEventListener('click', (e) => {
if (typeof (that[m.value]) === 'function') {
that[m.value](e)
}
});
removeName.push(m.name)
// elements[i].attributes.removeNamedItem(m.name)
break;
}
case '@change': {
isEvent = true
Event.push((e) => {
let value = e.target.value
if (e.target.type == 'number' && value != '') {
value = Number(value)
e.target.value = value
}
if (typeof (that[m.value]) === 'function') {
that[m.value](e, value)
}
})
break;
}
}
// elements[i].attributes[m] = undefined
}
for (let n = 0; n < removeName.length; n++) {
elements[i].attributes.removeNamedItem(removeName[n])
}
if (isEvent) {
let ventType = 'input'
if (elements[i].tagName != 'INPUT' || elements[i].type == 'checkbox') {
ventType = 'change'
}
elements[i].addEventListener(ventType, (e) => {
for (let t = 0; t < Event.length; t++) {
Event[t](e)
}
});
}
}
}
}
export default Submerge

View File

@ -0,0 +1,32 @@
function html() {
return `
<span class="custom-divider"></span>
<div class="div-item">
<div class="row">
<div class="col">
<span class="label" style="flex: 0 0 70px;">挖掘高度</span>
<div class="input-number input-number-unit-1">
<input class="input" type="number" title="" min="0" max="5000000" name="height">
<span class="unit">m</span>
<span class="arrow"></span>
</div>
</div>
</div>
</div>
<span class="custom-divider"></span>
<div class="div-item">
<div class="row">
<div class="col">
<span class="label">绘制开挖区域</span>
<button class="start-excavation"><svg class="icon-edit"><use xlink:href="#yj-icon-edit"></use></svg>绘制</button>
</div>
<div class="col">
<span class="label">清除开挖区域</span>
<button class="clean-excavation"><svg class="icon-close"><use xlink:href="#yj-icon-close"></use></svg>清除</button>
</div>
</div>
</div>
`
}
export { html }

View File

@ -0,0 +1,459 @@
import Tools from "../../../Tools";
import DrawPolygon from "../../../Draw/drawPolygon"
import Dialog from '../../../BaseDialog';
import { html } from "./_element";
let ExcavationFaces = []
class TerrainExcavation extends Tools {
/**
* @constructor
* @description 地形开挖
* @param sdk
* */
constructor(sdk, options = {}) {
super(sdk)
this.viewer = sdk.viewer
this.options = options || {};
this.options.height = (this.options.height || this.options.height === 0) ? this.options.height : 10;
this.options.show = (this.options.show || this.options.show === false) ? this.options.show : true;
this.bottomImg = this.getSourceRootPath() + '/img/excavationregion_top.jpg';
this.wallImg = this.getSourceRootPath() + '/img/excavationregion_side.jpg';
this.splitNum = Cesium.defaultValue(options.splitNum, 50);
this.Draw = new DrawPolygon(this.sdk)
this.bottomMaterial = Cesium.Material.fromType('Color', {
color: Cesium.Color.fromAlpha(Cesium.Color.fromCssColorString('#735d4f'))
})
this.wallMaterial = Cesium.Material.fromType('Color', {
color: Cesium.Color.fromAlpha(Cesium.Color.fromCssColorString('#976b4e'))
})
let imageBottom = new Image();
let wallBottom = new Image();
imageBottom.src = this.bottomImg;
wallBottom.src = this.wallImg;
imageBottom.crossOrigin = "Anonymous";
wallBottom.crossOrigin = "Anonymous";
imageBottom.onload = () => {
const canvas = document.createElement('canvas');
canvas.width = imageBottom.width;
canvas.height = imageBottom.height;
const context = canvas.getContext('2d');
context.drawImage(imageBottom, 0, 0, imageBottom.width, imageBottom.height);
const base64 = canvas.toDataURL('image/jpg');
this.bottomMaterial = new Cesium.Material({
fabric: {
type: "Image",
uniforms: {
image: base64,
},
},
});
if (this.bottomSurface) {
this.bottomSurface.appearance.material = this.bottomMaterial;
}
}
wallBottom.onload = () => {
const canvas = document.createElement('canvas');
canvas.width = wallBottom.width;
canvas.height = wallBottom.height;
const context = canvas.getContext('2d');
context.drawImage(wallBottom, 0, 0, wallBottom.width, wallBottom.height);
const base64 = canvas.toDataURL('image/jpg');
this.wallMaterial = new Cesium.Material({
fabric: {
type: "Image",
uniforms: {
image: base64,
},
},
});
if (this.wellWall) {
this.wellWall.appearance.material = this.wallMaterial;
}
}
this.init();
}
get show() {
return this.options.show
}
set show(v) {
this.options.show = v;
this.switchExcavate(v);
}
get height() {
return this.options.height
}
set height(v) {
this.options.height = v;
// this.updateExcavateDepth(v);
}
init() {
TerrainExcavation.edit(this, true)
}
static async edit(that, state) {
if (that._DialogObject && that._DialogObject.close) {
that._DialogObject.close()
that._DialogObject = null
}
if (state) {
that._DialogObject = await new Dialog(that.sdk.viewer._container, {
title: '地形开挖',
closeCallBack: () => {
},
})
await that._DialogObject.init()
that._DialogObject._element.body.className = that._DialogObject._element.body.className + ' terrain-excavation'
let contentElm = document.createElement('div');
contentElm.innerHTML = html()
that._DialogObject.contentAppChild(contentElm)
// 开始
let e_start = contentElm.getElementsByClassName('start-excavation')[0]
e_start.addEventListener('click', () => {
that.startCreate()
});
// 清除
let e_clean = contentElm.getElementsByClassName('clean-excavation')[0]
e_clean.addEventListener('click', () => {
that.clear()
});
// 高度值
let e_height = contentElm.querySelector("input[name='height']")
e_height.value = that.height
e_height.addEventListener('change', (e) => {
let value = e.target.value
value = Number(value)
if (value < 0.01) {
value = 0.01
e.target.value = value
that.height = value;
}
})
e_height.addEventListener('blur', (e) => {
let value = e.target.value
value = Number(value)
if ((e.target.max) && value > Number(e.target.max)) {
value = Number(e.target.max)
}
if (value < 0.01) {
value = 0.01
}
e.target.value = value
that.height = value;
});
} else {
if (that._DialogObject && that._DialogObject.close) {
that._DialogObject.close()
that._DialogObject = null
}
}
}
startCreate() {
this.Draw.start((e, positions) => {
if (!positions || positions.length <= 2) {
window.ELEMENT && window.ELEMENT.Message({
message: '至少拥有三个坐标位置!',
type: 'warning',
duration: 1500
});
return
}
if(!this.isConvex(positions)) {
window.ELEMENT && window.ELEMENT.Message({
message: '不支持凹多边形',
type: 'warning',
duration: 1500
});
console.log('不支持凹多边形')
return
}
this.updateData(positions)
})
}
updateData(activePoints) {
let viewer = this.viewer;
this.clear();
let clippingPlanesList = [];
let array = []
for (let i = 0; i < activePoints.length; i++) {
array.push([activePoints[i].lng, activePoints[i].lat])
}
array.push([activePoints[0].lng, activePoints[0].lat])
let clockwiseRing = turf.lineString(array);
// 是否顺时针
let boolDiff = turf.booleanClockwise(clockwiseRing);
this.excavateMinHeight = 9999;
for (let index = 0; index < activePoints.length; ++index) {
let s = (index + 1) % activePoints.length;
let position1 = Cesium.Cartesian3.fromDegrees(activePoints[index].lng, activePoints[index].lat, activePoints[index].alt)
let position2 = Cesium.Cartesian3.fromDegrees(activePoints[s].lng, activePoints[s].lat, activePoints[s].alt)
let curMidPoint = Cesium.Cartesian3.midpoint(
position1,
position2,
new Cesium.Cartesian3()
);
let cartographic = Cesium.Cartographic.fromCartesian(position1);
let curHeight =
viewer.scene.globe.getHeight(cartographic) || cartographic.height;
if (curHeight < this.excavateMinHeight) {
this.excavateMinHeight = curHeight;
}
let curMidPointNormal = Cesium.Cartesian3.normalize(
curMidPoint,
new Cesium.Cartesian3()
);
let curMidPointDifference = boolDiff
? Cesium.Cartesian3.subtract(
position1,
curMidPoint,
new Cesium.Cartesian3()
)
: Cesium.Cartesian3.subtract(
position2,
curMidPoint,
new Cesium.Cartesian3()
);
curMidPointDifference = Cesium.Cartesian3.normalize(
curMidPointDifference,
curMidPointDifference
);
let curMidPointCross = Cesium.Cartesian3.cross(
curMidPointDifference,
curMidPointNormal,
new Cesium.Cartesian3()
);
curMidPointCross = Cesium.Cartesian3.normalize(
curMidPointCross,
curMidPointCross
);
let plane = new Cesium.Plane(curMidPointCross, 0);
let distance = Cesium.Plane.getPointDistance(plane, curMidPoint);
clippingPlanesList.push(
new Cesium.ClippingPlane(curMidPointCross, distance)
);
}
this.viewer.scene.globe.clippingPlanes = new Cesium.ClippingPlaneCollection(
{
planes: clippingPlanesList,
edgeWidth: 1,
edgeColor: Cesium.Color.WHITE,
enabled: true,
}
);
this.prepareWell(activePoints);
this.createWell(this.wellData);
// this.viewer.entities.remove(this.drawGeomtry);
}
clear() {
if (this.viewer.scene.globe.clippingPlanes) {
this.viewer.scene.globe.clippingPlanes.removeAll();
this.viewer.scene.primitives.remove(this.bottomSurface);
this.viewer.scene.primitives.remove(this.wellWall);
this.viewer.scene.render();
}
for (let i = ExcavationFaces.length - 1; i >= 0; i--) {
this.viewer.scene.primitives.remove(ExcavationFaces[i]);
}
ExcavationFaces = []
this.Draw && this.Draw.end()
}
destroy() {
this.clear()
}
//计算并更新wellData
prepareWell(activePoints) {
let pointLength = activePoints.length;
let heightDiff = this.excavateMinHeight - this.height;
let no_height_top = [],
bottom_pos = [],
lerp_pos = [];
for (let l = 0; l < pointLength; l++) {
let u = l == pointLength - 1 ? 0 : l + 1;
let point0 = [
Cesium.Cartographic.fromDegrees(activePoints[l].lng, activePoints[l].lat, activePoints[l].alt).longitude,
Cesium.Cartographic.fromDegrees(activePoints[l].lng, activePoints[l].lat, activePoints[l].alt).latitude,
];
let point1 = [
Cesium.Cartographic.fromDegrees(activePoints[u].lng, activePoints[u].lat, activePoints[u].alt).longitude,
Cesium.Cartographic.fromDegrees(activePoints[u].lng, activePoints[u].lat, activePoints[u].alt).latitude,
];
if (0 == l) {
lerp_pos.push(new Cesium.Cartographic(point0[0], point0[1]));
bottom_pos.push(
Cesium.Cartesian3.fromRadians(point0[0], point0[1], heightDiff)
);
no_height_top.push(
Cesium.Cartesian3.fromRadians(point0[0], point0[1], 0)
);
}
for (let p = 1; p <= this.splitNum; p++) {
let m = Cesium.Math.lerp(point0[0], point1[0], p / this.splitNum);
let g = Cesium.Math.lerp(point0[1], point1[1], p / this.splitNum);
(l == pointLength - 1 && p == this.splitNum) ||
(lerp_pos.push(new Cesium.Cartographic(m, g)),
bottom_pos.push(Cesium.Cartesian3.fromRadians(m, g, heightDiff)),
no_height_top.push(Cesium.Cartesian3.fromRadians(m, g, 0)));
}
}
this.wellData = {
lerp_pos: lerp_pos,
bottom_pos: bottom_pos,
no_height_top: no_height_top,
};
}
//开始创建底面和侧面
createWell(wallData) {
let $this = this;
if (this.viewer.terrainProvider._layers) {
this.createBottomSurface(wallData.bottom_pos);
let positions = Cesium.sampleTerrainMostDetailed(
this.viewer.terrainProvider,
wallData.lerp_pos
);
positions.then((pos) => {
let positionList = [];
for (let index = 0; index < pos.length; index++) {
const element = pos[index];
let curPos = Cesium.Cartesian3.fromRadians(
element.longitude,
element.latitude,
element.height
);
positionList.push(curPos);
}
$this.createWellWall(wallData.bottom_pos, positionList);
});
} else {
this.createBottomSurface(wallData.bottom_pos);
this.createWellWall(wallData.bottom_pos, wallData.no_height_top);
}
}
//坐标转换,转出经纬度格式
ellipsoidToDegree(pos) {
let cartesian3 = new Cesium.Cartesian3(pos.x, pos.y, pos.z);
let cartographic =
this.viewer.scene.globe.ellipsoid.cartesianToCartographic(cartesian3);
return {
longitude: Cesium.Math.toDegrees(cartographic.longitude),
latitude: Cesium.Math.toDegrees(cartographic.latitude),
altitude: cartographic.height,
};
}
//创建地形开挖的底面对象
createBottomSurface(points) {
if (points.length) {
let minHeight = this.getMinHeight(points);
let positions = [];
for (let i = 0; i < points.length; i++) {
let curPoint = this.ellipsoidToDegree(points[i]);
positions.push(curPoint.longitude, curPoint.latitude, minHeight);
}
let polygon = new Cesium.PolygonGeometry({
polygonHierarchy: new Cesium.PolygonHierarchy(
Cesium.Cartesian3.fromDegreesArrayHeights(positions)
),
perPositionHeight: true,
});
let appearance = new Cesium.MaterialAppearance({
translucent: false,
flat: true,
material: this.bottomMaterial,
});
this.bottomSurface = new Cesium.Primitive({
geometryInstances: new Cesium.GeometryInstance({
geometry: Cesium.PolygonGeometry.createGeometry(polygon),
}),
appearance: appearance,
asynchronous: false,
});
ExcavationFaces.push(this.bottomSurface);
this.viewer.scene.primitives.add(this.bottomSurface);
}
}
// 创建地形开挖的侧面墙对象
createWellWall(bottomPos, positionList) {
let minHeight = this.getMinHeight(bottomPos);
let maxHeights = [],
minHeights = [];
for (let i = 0; i < positionList.length; i++) {
maxHeights.push(this.ellipsoidToDegree(positionList[i]).altitude);
minHeights.push(minHeight);
}
let wall = new Cesium.WallGeometry({
positions: positionList,
maximumHeights: maxHeights,
minimumHeights: minHeights,
});
let geometry = Cesium.WallGeometry.createGeometry(wall);
let appearance = new Cesium.MaterialAppearance({
translucent: false,
flat: true,
material: this.wallMaterial,
});
this.wellWall = new Cesium.Primitive({
geometryInstances: new Cesium.GeometryInstance({
geometry: geometry,
attributes: {
color: Cesium.ColorGeometryInstanceAttribute.fromColor(
Cesium.Color.GREY
),
},
id: "PitWall",
}),
appearance: appearance,
asynchronous: false,
});
ExcavationFaces.push(this.wellWall);
this.viewer.scene.primitives.add(this.wellWall);
}
//获取地形开挖最低点高程值
getMinHeight(points) {
let minHeight = 5000000;
let minPoint = null;
for (let i = 0; i < points.length; i++) {
let height = points[i]["z"];
if (height < minHeight) {
minHeight = height;
minPoint = this.ellipsoidToDegree(points[i]);
}
}
return minPoint.altitude;
}
switchExcavate(show) {
if (show) {
this.viewer.scene.globe.material = null;
this.wellWall.show = true;
this.bottomSurface.show = true;
} else {
this.viewer.scene.globe.material = null;
this.wellWall.show = false;
this.bottomSurface.show = false;
}
}
updateExcavateDepth(height) {
this.viewer.scene.primitives.remove(this.bottomSurface);
this.viewer.scene.primitives.remove(this.wellWall);
if (!this.wellData) {
return
}
let lerp_pos = this.wellData.lerp_pos;
let posList = [];
for (let n = 0; n < lerp_pos.length; n++) {
posList.push(
Cesium.Cartesian3.fromRadians(
lerp_pos[n].longitude,
lerp_pos[n].latitude,
this.excavateMinHeight - height
)
);
}
this.wellData.bottom_pos = posList;
this.createWell(this.wellData);
}
}
export default TerrainExcavation;

View File

@ -0,0 +1,54 @@
function html() {
return `
<span class="custom-divider"></span>
<div class="div-item">
<div class="row">
<div class="col">
<span class="label">位置拾取(起点、终点)</span>
<button class="edit"><svg class="icon-edit"><use xlink:href="#yj-icon-edit"></use></svg>拾取</button>
</div>
</div>
</div>
<span class="custom-divider"></span>
<div class="div-item">
<div class="row" style="margin-bottom: 25px;">
<div class="col">
<span class="label">视点高度</span>
<div class="input-number input-number-unit-1">
<input class="input" type="number" title="" min="0" max="999999" step="0.1" @model="viewPointHeight">
<span class="unit">m</span>
<span class="arrow"></span>
</div>
</div>
<div class="col">
</div>
</div>
<div class="row subtitle-box">
<span class="subtitle">视域夹角</span>
</div>
<div class="row">
<div class="col">
<div class="range-box">
<div class="range-bg">
<div class="range-process-box">
<div class="range-process"></div>
</div>
</div>
<div class="range-node-box">
<span class="range-node-text">0°</span>
<span class="range-node-text">45°</span>
<span class="range-node-text">90°</span>
<span class="range-node-text">135°</span>
<span class="range-node-text">180°</span>
<div class="range-node-active"><span class="range-node-active-text">0°</span></div>
</div>
<input type="range" max="180" min="0" step="1" name="horizontalViewAngle">
</div>
</div>
</div>
</div>
<span class="custom-divider"></span>
`
}
export { html }

View File

@ -0,0 +1,511 @@
/*
* @Author: Wang jianLei
* @Date: 2022-05-17 21:49:28
* @Last Modified by: Wang JianLei
* @Last Modified time: 2022-05-19 22:08:14
*/
let ViewShed = function (sdk, canvasEleId) {
if (!sdk.viewer) throw new Error("no viewer object!");
let canvasEle = document.getElementById(canvasEleId);
if (!canvasEle) throw new Error("the canvas element is not exist");
this.canvasEle = canvasEle;
this.viewer = sdk.viewer;
this.handler = undefined;
this.lightCamera;
this.pyramid;
this.frustumPrimitive;
this.viewershedPolygon;
};
ViewShed.prototype = {
/**
* 初始化handler
*/
initHandler() {
if (this.handler) {
this.handler.destroy();
this.handler = undefined;
}
},
/**
* 开始执行视域分析
* @param {number} precision 精度值越大创建耗时越长建议在10~20之间
*/
createViewshed: function (precision) {
let $this = this;
let scene = $this.viewer.scene;
$this.initHandler();
$this.clearAll();
$this.handler = new Cesium.ScreenSpaceEventHandler($this.viewer.canvas);
$this.handler.setInputAction((event) => {
// 禁止地球旋转和缩放,地球的旋转会对鼠标移动监听有影响,所以需要禁止
scene.screenSpaceCameraController.enableRotate = false;
scene.screenSpaceCameraController.enableZoom = false;
scene.globe.depthTestAgainstTerrain = true;
let earthPosition = scene.pickPosition(event.position);
let pos = $this.cartesian3ToDegree(earthPosition);
$this.handler.setInputAction(function (event) {
let newPosition = scene.pickPosition(event.endPosition);
if (Cesium.defined(newPosition)) {
let pos1 = $this.cartesian3ToDegree(newPosition);
let distance = Cesium.Cartesian3.distance(newPosition, earthPosition);
let angle = $this.getAngle(pos[0], pos[1], pos1[0], pos1[1]);
let pitch = $this.getPitch(earthPosition, newPosition);
$this.ViewShedOptions = {
viewPosition: earthPosition, //观测点 笛卡尔坐标
endPosition: newPosition, //目标点 笛卡尔坐标
direction: angle, //观测方位角 默认为`0`,范围`0~360`
pitch: pitch, //俯仰角,radius,默认为`0`
horizontalViewAngle: 90, //可视域水平夹角,默认为 `90`,范围`0~360`
verticalViewAngle: 60, //可视域垂直夹角,默认为`60`,范围`0~180`
visibleAreaColor: Cesium.Color.GREEN, //可视区域颜色,默认为`green`
invisibleAreaColor: Cesium.Color.RED, //不可见区域颜色,默认为`red`
visualRange: distance, //距离,单位`米`
};
$this.updateViewShed();
}
}, Cesium.ScreenSpaceEventType.MOUSE_MOVE);
}, Cesium.ScreenSpaceEventType.LEFT_DOWN);
$this.handler.setInputAction(() => {
$this.initHandler();
// 开启地球旋转和缩放
scene.screenSpaceCameraController.enableRotate = true;
scene.screenSpaceCameraController.enableZoom = true;
$this.drawViewershed(precision);
}, Cesium.ScreenSpaceEventType.LEFT_UP);
},
ReturnDistance(pos0, pos1) {
let distance = 0;
let point1cartographic = Cesium.Cartographic.fromCartesian(pos0);
let point2cartographic = Cesium.Cartographic.fromCartesian(pos1);
/**根据经纬度计算出距离**/
let geodesic = new Cesium.EllipsoidGeodesic();
geodesic.setEndPoints(point1cartographic, point2cartographic);
let s = geodesic.surfaceDistance;
return s;
},
getHeight(x, y, objectsToExclude) {
let endCartographic = Cesium.Cartographic.fromDegrees(x, y);
let endHeight = this.viewer.scene.sampleHeight(
endCartographic,
objectsToExclude
);
return endHeight;
},
cartesian3ToDegree: function (Cartesian3) {
let _ellipsoid = this.viewer.scene.globe.ellipsoid;
let _cartographic = _ellipsoid.cartesianToCartographic(Cartesian3);
let _lat = Cesium.Math.toDegrees(_cartographic.latitude);
let _lng = Cesium.Math.toDegrees(_cartographic.longitude);
let _alt = _cartographic.height;
return [_lng, _lat, _alt];
},
getAngle: function (lng1, lat1, lng2, lat2) {
let dRotateAngle = Math.atan2(Math.abs(lng1 - lng2), Math.abs(lat1 - lat2));
if (lng2 >= lng1) {
dRotateAngle = lat2 < lat1 ? Math.PI - dRotateAngle : dRotateAngle;
} else {
dRotateAngle =
lat2 >= lat1 ? 2 * Math.PI - dRotateAngle : Math.PI + dRotateAngle;
}
dRotateAngle = (dRotateAngle * 180) / Math.PI;
return dRotateAngle;
},
getPitch(pointA, pointB) {
let transfrom = Cesium.Transforms.eastNorthUpToFixedFrame(pointA);
const vector = Cesium.Cartesian3.subtract(
pointB,
pointA,
new Cesium.Cartesian3()
);
let direction = Cesium.Matrix4.multiplyByPointAsVector(
Cesium.Matrix4.inverse(transfrom, transfrom),
vector,
vector
);
Cesium.Cartesian3.normalize(direction, direction);
return Cesium.Math.PI_OVER_TWO - Cesium.Math.acosClamped(direction.z);
},
updateViewShed: function () {
this.clear();
this.setLightCamera();
this.addVisualPyramid();
this.createFrustum();
},
clear: function () {
if (this.pyramid) {
this.viewer.entities.removeById(this.pyramid.id);
this.pyramid = undefined;
}
if (this.frustumPrimitive) {
this.viewer.scene.primitives.remove(this.frustumPrimitive);
this.frustumPrimitive = undefined;
}
if (this.debugModelMatrixPrimitive) {
this.viewer.scene.primitives.remove(this.debugModelMatrixPrimitive);
this.debugModelMatrixPrimitive = undefined;
}
},
clearAll: function () {
this.clear();
if (this.viewershedPolygon) {
this.viewer.scene.primitives.remove(this.viewershedPolygon);
this.viewershedPolygon = undefined;
}
},
addVisualPyramid: function () {
let options = this.ViewShedOptions;
let position = options.viewPosition;
let visualRange = Number(options.visualRange);
let transform = Cesium.Transforms.eastNorthUpToFixedFrame(position);
this.debugModelMatrixPrimitive = this.viewer.scene.primitives.add(
new Cesium.DebugModelMatrixPrimitive({
modelMatrix: transform,
length: 5.0,
})
);
const halfClock = options.horizontalViewAngle / 2;
const halfCone = options.verticalViewAngle / 2;
const pitch = Cesium.Math.toDegrees(options.pitch);
const ellipsoid = new Cesium.EllipsoidGraphics({
radii: new Cesium.Cartesian3(visualRange, visualRange, visualRange),
minimumClock: Cesium.Math.toRadians(90 - options.direction - halfClock),
maximumClock: Cesium.Math.toRadians(90 - options.direction + halfClock),
minimumCone: Cesium.Math.toRadians(90 - pitch - halfCone),
maximumCone: Cesium.Math.toRadians(90 - pitch + halfCone),
fill: false,
outline: true,
subdivisions: 256,
stackPartitions: 64,
slicePartitions: 64,
outlineColor: Cesium.Color.YELLOWGREEN.withAlpha(0.5),
});
const pyramidEntity = new Cesium.Entity({
position: position,
ellipsoid,
});
this.pyramid = this.viewer.entities.add(pyramidEntity);
},
setLightCamera: function () {
if (!this.lightCamera) {
this.lightCamera = new Cesium.Camera(this.viewer.scene);
}
let options = this.ViewShedOptions;
let visualRange = Number(options.visualRange);
this.lightCamera.position = options.viewPosition;
this.lightCamera.frustum.near = 0.1;
this.lightCamera.frustum.far = visualRange;
const hr = Cesium.Math.toRadians(options.horizontalViewAngle);
const vr = Cesium.Math.toRadians(options.verticalViewAngle);
this.lightCamera.frustum.aspectRatio =
(visualRange * Math.tan(hr / 2) * 2) /
(visualRange * Math.tan(vr / 2) * 2);
this.lightCamera.frustum.fov = hr > vr ? hr : vr;
this.lightCamera.setView({
destination: options.viewPosition,
orientation: {
heading: Cesium.Math.toRadians(options.direction || 0),
pitch: options.pitch || 0,
roll: 0,
},
});
},
createFrustum: function () {
const scratchRight = new Cesium.Cartesian3();
const scratchRotation = new Cesium.Matrix3();
const scratchOrientation = new Cesium.Quaternion();
const direction = this.lightCamera.directionWC;
const up = this.lightCamera.upWC;
let right = this.lightCamera.rightWC;
right = Cesium.Cartesian3.negate(right, scratchRight);
let rotation = scratchRotation;
Cesium.Matrix3.setColumn(rotation, 0, right, rotation);
Cesium.Matrix3.setColumn(rotation, 1, up, rotation);
Cesium.Matrix3.setColumn(rotation, 2, direction, rotation);
let orientation = Cesium.Quaternion.fromRotationMatrix(
rotation,
scratchOrientation
);
let instanceOutline = new Cesium.GeometryInstance({
geometry: new Cesium.FrustumOutlineGeometry({
frustum: this.lightCamera.frustum,
origin: this.ViewShedOptions.viewPosition,
orientation: orientation,
}),
id: "视椎体轮廓线" + Math.random().toString(36).substr(2),
attributes: {
color: Cesium.ColorGeometryInstanceAttribute.fromColor(
new Cesium.Color(0.0, 1.0, 0.0, 1.0)
),
show: new Cesium.ShowGeometryInstanceAttribute(true),
},
});
this.frustumPrimitive = this.viewer.scene.primitives.add(
new Cesium.Primitive({
geometryInstances: instanceOutline,
appearance: new Cesium.PerInstanceColorAppearance({
flat: true,
translucent: false,
closed: true,
}),
})
);
},
createPoint: function (firstPos, secondPos) {
let entity4FirstPos = new Cesium.Entity({
name: "firstPos",
show: true,
position: firstPos,
point: {
show: true,
pixelSize: 20,
color: Cesium.Color.RED,
outlineColor: Cesium.Color.YELLOW,
outlineWidth: 5,
},
description: `
<p>这是绘制的视椎体起点</p>`,
});
this.viewer.entities.add(entity4FirstPos);
let entity4SecondPos = new Cesium.Entity({
name: "secondPos",
show: true,
position: secondPos,
point: {
show: true,
pixelSize: 30,
color: Cesium.Color.YELLOW,
outlineColor: Cesium.Color.RED,
outlineWidth: 8,
},
description: `
<p>这是绘制的视椎体视角终点</p>`,
});
this.viewer.entities.add(entity4SecondPos);
},
//绘制可视域
add(positionArr) {
let polygon = new Cesium.PolygonGeometry({
polygonHierarchy: new Cesium.PolygonHierarchy(
Cesium.Cartesian3.fromDegreesArray(positionArr)
),
height: 0.0,
extrudedHeight: 0.0,
vertexFormat: Cesium.PerInstanceColorAppearance.VERTEX_FORMAT,
stRotation: 0.0, // 纹理的旋转坐标(以弧度为单位),正旋转是逆时针方向
ellipsoid: Cesium.Ellipsoid.WGS84,
granularity: Cesium.Math.RADIANS_PER_DEGREE, // 每个纬度和经度之间的距离(以弧度为单位),确定缓冲区中的位置数
perPositionHeight: false, // 每个位置点使用的高度
closeTop: true,
closeBottom: true,
// NONE 与椭圆表面不符的直线;GEODESIC 遵循测地路径;RHUMB 遵循大黄蜂或恶魔般的道路。
arcType: Cesium.ArcType.GEODESIC, // 多边形边缘线型
});
let polygonInstance = new Cesium.GeometryInstance({
geometry: polygon,
name: "ViewershedPolygon",
attributes: {
color: Cesium.ColorGeometryInstanceAttribute.fromColor(
Cesium.Color.BLUE.withAlpha(0.6)
),
show: new Cesium.ShowGeometryInstanceAttribute(true), //显示或者隐藏
},
});
this.viewershedPolygon = this.viewer.scene.primitives.add(
new Cesium.GroundPrimitive({
geometryInstances: polygonInstance,
appearance: new Cesium.EllipsoidSurfaceAppearance({
aboveGround: true,
material: new Cesium.Material({
fabric: {
type: "Image",
uniforms: {
image: this.returnImgae(),
},
},
}),
}),
})
);
},
drawViewershed(precision) {
const pos = this.cartesian3ToDegree(this.ViewShedOptions.viewPosition);
const radius = this.ViewShedOptions.visualRange;
const direction = this.ViewShedOptions.direction;
let boundary = this.computeBoundaryOptions(pos, radius, direction);
const bbox = boundary.bbox;
let mask = turf.polygon([boundary.boundaryPoints]);
const dis = this.ViewShedOptions.visualRange / (precision * 1000);
let gridPoints = turf.pointGrid(bbox, dis, { mask: mask });
let pointsResult = this.createTargetPoints(gridPoints, dis, pos);
let variogram = kriging.train(
pointsResult.values,
pointsResult.lngs,
pointsResult.lats,
"exponential",
0,
100
);
let grid = kriging.grid([boundary.boundaryPoints], variogram, dis / 1000);
const colors = [
"#ff000080",
"#ff000080",
"#ff000080",
"#ff000080",
"#ff000080",
"#ff000080",
"#00ff0080",
"#00ff0080",
"#00ff0080",
"#00ff0080",
"#00ff0080",
"#00ff0080",
];
this.canvasEle.width = 3840;
this.canvasEle.height = 2160;
kriging.plot(
this.canvasEle,
grid,
[bbox[0], bbox[2]],
[bbox[1], bbox[3]],
colors
);
this.add(boundary.positionArr);
},
computeBoundaryOptions(pos, radius, angle) {
let Ea = 6378137; // 赤道半径
let Eb = 6356725; // 极半径
const lng = pos[0],
lat = pos[1];
const bbox = [lng, lat, lng, lat]; //[minX, minY, maxX, maxY]
let positionArr = [];
let boundaryPoints = [];
positionArr.push(lng, lat);
boundaryPoints.push([lng, lat]);
//正北是0°
let start = angle + 45 > 360 ? angle - 45 - 360 : angle - 45;
let end = start + 90;
for (let i = start; i <= end; i++) {
let dx = radius * Math.sin((i * Math.PI) / 180.0);
let dy = radius * Math.cos((i * Math.PI) / 180.0);
let ec = Eb + ((Ea - Eb) * (90.0 - lat)) / 90.0;
let ed = ec * Math.cos((lat * Math.PI) / 180);
let BJD = lng + ((dx / ed) * 180.0) / Math.PI;
let BWD = lat + ((dy / ec) * 180.0) / Math.PI;
positionArr.push(BJD, BWD);
boundaryPoints.push([BJD, BWD]);
this.refreshBBox(bbox, BJD, BWD);
}
boundaryPoints.push([lng, lat]);
return {
positionArr,
boundaryPoints,
bbox,
};
},
/**
* 更新外围矩形 Bbox
* @param {Array} result 外围矩形Bbox-[minX, minY, maxX, maxY]
* @param {Number} x 经度
* @param {Number} y 纬度
*/
refreshBBox(result, x, y) {
result[0] = x < result[0] ? x : result[0];
result[1] = y < result[1] ? y : result[1];
result[2] = x > result[2] ? x : result[2];
result[3] = y > result[3] ? y : result[3];
},
/**
* 插值点用射线判断通视性
* @param {*} gridPoints 网格点
* @param {*} step 步长,可以理解成是精度
* @param {*} sourcePos 视域分析起点
* @returns kriging插值所需的参数对象{ values:[], lngs:[], lats:[]}
*/
createTargetPoints(gridPoints, step, sourcePos) {
let positionArr = [];
let objectsToExclude = [
this.frustumPrimitive,
this.pyramid,
this.debugModelMatrixPrimitive,
];
let values = [],
lngs = [],
lats = [];
let height = this.getHeight(sourcePos[0], sourcePos[1], objectsToExclude);
positionArr.push({
x: sourcePos[0],
y: sourcePos[1],
z: height,
});
let viewPoint = this.ViewShedOptions.viewPosition;
for (let index = 0; index < gridPoints.features.length; index++) {
const feature = gridPoints.features[index];
const coords = feature.geometry.coordinates;
const x = coords[0],
y = coords[1];
let h = this.getHeight(x, y, objectsToExclude);
let endPoint = Cesium.Cartesian3.fromDegrees(x, y, h);
let direction = Cesium.Cartesian3.normalize(
Cesium.Cartesian3.subtract(
endPoint,
viewPoint,
new Cesium.Cartesian3()
),
new Cesium.Cartesian3()
);
// 建立射线
let ray = new Cesium.Ray(viewPoint, direction);
let result = this.viewer.scene.pickFromRay(ray, objectsToExclude); // 计算交互点,返回第一个
if (result) {
let buffer = this.ReturnDistance(endPoint, result.position);
// let M_color = Cesium.Color.GREEN;
if (buffer > step) {
// M_color = Cesium.Color.RED;
values.push(0);
} else {
values.push(1);
}
lngs.push(x);
lats.push(y);
// this.viewer.entities.add(
// new Cesium.Entity({
// name: "插值点哦",
// show: true,
// position: endPoint,
// point: {
// show: true,
// pixelSize: 10,
// color: M_color,
// outlineWidth: 2,
// outlineColor: Cesium.Color.YELLOW,
// },
// })
// );
}
}
return {
values,
lngs,
lats,
};
},
/**
* canvas转image图片
* @returns base64图片
*/
returnImgae() {
return this.canvasEle.toDataURL("image/png");
},
};
export default ViewShed;

View File

@ -0,0 +1,131 @@
export default `
#define USE_CUBE_MAP_SHADOW true
uniform sampler2D colorTexture;
uniform sampler2D depthTexture;
varying vec2 v_textureCoordinates;
uniform mat4 camera_projection_matrix;
uniform mat4 camera_view_matrix;
uniform samplerCube shadowMap_textureCube;
uniform mat4 shadowMap_matrix;
uniform vec4 shadowMap_lightPositionEC;
uniform vec4 shadowMap_normalOffsetScaleDistanceMaxDistanceAndDarkness;
uniform vec4 shadowMap_texelSizeDepthBiasAndNormalShadingSmooth;
uniform float helsing_viewDistance;
uniform vec4 helsing_visibleAreaColor;
uniform vec4 helsing_invisibleAreaColor;
struct zx_shadowParameters
{
vec3 texCoords;
float depthBias;
float depth;
float nDotL;
vec2 texelStepSize;
float normalShadingSmooth;
float darkness;
};
float czm_shadowVisibility(samplerCube shadowMap, zx_shadowParameters shadowParameters)
{
float depthBias = shadowParameters.depthBias;
float depth = shadowParameters.depth;
float nDotL = shadowParameters.nDotL;
float normalShadingSmooth = shadowParameters.normalShadingSmooth;
float darkness = shadowParameters.darkness;
vec3 uvw = shadowParameters.texCoords;
depth -= depthBias;
float visibility = czm_shadowDepthCompare(shadowMap, uvw, depth);
return czm_private_shadowVisibility(visibility, nDotL, normalShadingSmooth, darkness);
}
vec4 getPositionEC(){
return czm_windowToEyeCoordinates(gl_FragCoord);
}
vec3 getNormalEC(){
return vec3(1.);
}
vec4 toEye(in vec2 uv,in float depth){
vec2 xy=vec2((uv.x*2.-1.),(uv.y*2.-1.));
vec4 posInCamera=czm_inverseProjection*vec4(xy,depth,1.);
posInCamera=posInCamera/posInCamera.w;
return posInCamera;
}
vec3 pointProjectOnPlane(in vec3 planeNormal,in vec3 planeOrigin,in vec3 point){
vec3 v01=point-planeOrigin;
float d=dot(planeNormal,v01);
return(point-planeNormal*d);
}
float getDepth(in vec4 depth){
float z_window=czm_unpackDepth(depth);
z_window=czm_reverseLogDepth(z_window);
float n_range=czm_depthRange.near;
float f_range=czm_depthRange.far;
return(2.*z_window-n_range-f_range)/(f_range-n_range);
}
float shadow(in vec4 positionEC){
vec3 normalEC=getNormalEC();
zx_shadowParameters shadowParameters;
shadowParameters.texelStepSize=shadowMap_texelSizeDepthBiasAndNormalShadingSmooth.xy;
shadowParameters.depthBias=shadowMap_texelSizeDepthBiasAndNormalShadingSmooth.z;
shadowParameters.normalShadingSmooth=shadowMap_texelSizeDepthBiasAndNormalShadingSmooth.w;
shadowParameters.darkness=shadowMap_normalOffsetScaleDistanceMaxDistanceAndDarkness.w;
vec3 directionEC=positionEC.xyz-shadowMap_lightPositionEC.xyz;
float distance=length(directionEC);
directionEC=normalize(directionEC);
float radius=shadowMap_lightPositionEC.w;
if(distance>radius)
{
return 2.0;
}
vec3 directionWC=czm_inverseViewRotation*directionEC;
shadowParameters.depth=distance/radius-0.0003;
shadowParameters.nDotL=clamp(dot(normalEC,-directionEC),0.,1.);
shadowParameters.texCoords=directionWC;
float visibility=czm_shadowVisibility(shadowMap_textureCube,shadowParameters);
return visibility;
}
bool visible(in vec4 result)
{
result.x/=result.w;
result.y/=result.w;
result.z/=result.w;
return result.x>=-1.&&result.x<=1.
&&result.y>=-1.&&result.y<=1.
&&result.z>=-1.&&result.z<=1.;
}
void main(){
// 釉色 = 结构二维(颜色纹理, 纹理坐标)
gl_FragColor = texture2D(colorTexture, v_textureCoordinates);
// 深度 = 获取深度(结构二维(深度纹理, 纹理坐标))
float depth = getDepth(texture2D(depthTexture, v_textureCoordinates));
// 视角 = (纹理坐标, 深度)
vec4 viewPos = toEye(v_textureCoordinates, depth);
// 世界坐标
vec4 wordPos = czm_inverseView * viewPos;
// 虚拟相机中坐标
vec4 vcPos = camera_view_matrix * wordPos;
float near = .001 * helsing_viewDistance;
float dis = length(vcPos.xyz);
if(dis > near && dis < helsing_viewDistance){
// 透视投影
vec4 posInEye = camera_projection_matrix * vcPos;
// 可视区颜色
// vec4 helsing_visibleAreaColor=vec4(0.,1.,0.,.5);
// vec4 helsing_invisibleAreaColor=vec4(1.,0.,0.,.5);
if(visible(posInEye)){
float vis = shadow(viewPos);
if(vis > 0.3){
gl_FragColor = mix(gl_FragColor,helsing_visibleAreaColor,.5);
} else{
gl_FragColor = mix(gl_FragColor,helsing_invisibleAreaColor,.5);
}
}
}
}`;

View File

@ -0,0 +1,131 @@
export default `
#define USE_CUBE_MAP_SHADOW true
uniform sampler2D colorTexture;
uniform sampler2D depthTexture;
in vec2 v_textureCoordinates;
uniform mat4 camera_projection_matrix;
uniform mat4 camera_view_matrix;
uniform samplerCube shadowMap_textureCube;
uniform mat4 shadowMap_matrix;
uniform vec4 shadowMap_lightPositionEC;
uniform vec4 shadowMap_normalOffsetScaleDistanceMaxDistanceAndDarkness;
uniform vec4 shadowMap_texelSizeDepthBiasAndNormalShadingSmooth;
uniform float helsing_viewDistance;
uniform vec4 helsing_visibleAreaColor;
uniform vec4 helsing_invisibleAreaColor;
struct zx_shadowParameters
{
vec3 texCoords;
float depthBias;
float depth;
float nDotL;
vec2 texelStepSize;
float normalShadingSmooth;
float darkness;
};
float czm_shadowVisibility(samplerCube shadowMap, zx_shadowParameters shadowParameters)
{
float depthBias = shadowParameters.depthBias;
float depth = shadowParameters.depth;
float nDotL = shadowParameters.nDotL;
float normalShadingSmooth = shadowParameters.normalShadingSmooth;
float darkness = shadowParameters.darkness;
vec3 uvw = shadowParameters.texCoords;
depth -= depthBias;
float visibility = czm_shadowDepthCompare(shadowMap, uvw, depth);
return czm_private_shadowVisibility(visibility, nDotL, normalShadingSmooth, darkness);
}
vec4 getPositionEC(){
return czm_windowToEyeCoordinates(gl_FragCoord);
}
vec3 getNormalEC(){
return vec3(1.);
}
vec4 toEye(in vec2 uv,in float depth){
vec2 xy=vec2((uv.x*2.-1.),(uv.y*2.-1.));
vec4 posInCamera=czm_inverseProjection*vec4(xy,depth,1.);
posInCamera=posInCamera/posInCamera.w;
return posInCamera;
}
vec3 pointProjectOnPlane(in vec3 planeNormal,in vec3 planeOrigin,in vec3 point){
vec3 v01=point-planeOrigin;
float d=dot(planeNormal,v01);
return(point-planeNormal*d);
}
float getDepth(in vec4 depth){
float z_window=czm_unpackDepth(depth);
z_window=czm_reverseLogDepth(z_window);
float n_range=czm_depthRange.near;
float f_range=czm_depthRange.far;
return(2.*z_window-n_range-f_range)/(f_range-n_range);
}
float shadow(in vec4 positionEC){
vec3 normalEC=getNormalEC();
zx_shadowParameters shadowParameters;
shadowParameters.texelStepSize=shadowMap_texelSizeDepthBiasAndNormalShadingSmooth.xy;
shadowParameters.depthBias=shadowMap_texelSizeDepthBiasAndNormalShadingSmooth.z;
shadowParameters.normalShadingSmooth=shadowMap_texelSizeDepthBiasAndNormalShadingSmooth.w;
shadowParameters.darkness=shadowMap_normalOffsetScaleDistanceMaxDistanceAndDarkness.w;
vec3 directionEC=positionEC.xyz-shadowMap_lightPositionEC.xyz;
float distance=length(directionEC);
directionEC=normalize(directionEC);
float radius=shadowMap_lightPositionEC.w;
if(distance>radius)
{
return 2.0;
}
vec3 directionWC=czm_inverseViewRotation*directionEC;
shadowParameters.depth=distance/radius-0.0003;
shadowParameters.nDotL=clamp(dot(normalEC,-directionEC),0.,1.);
shadowParameters.texCoords=directionWC;
float visibility=czm_shadowVisibility(shadowMap_textureCube,shadowParameters);
return visibility;
}
bool visible(in vec4 result)
{
result.x/=result.w;
result.y/=result.w;
result.z/=result.w;
return result.x>=-1.&&result.x<=1.
&&result.y>=-1.&&result.y<=1.
&&result.z>=-1.&&result.z<=1.;
}
void main(){
// 釉色 = 结构二维(颜色纹理, 纹理坐标)
out_FragColor = texture(colorTexture, v_textureCoordinates);
// 深度 = 获取深度(结构二维(深度纹理, 纹理坐标))
float depth = getDepth(texture(depthTexture, v_textureCoordinates));
// 视角 = (纹理坐标, 深度)
vec4 viewPos = toEye(v_textureCoordinates, depth);
// 世界坐标
vec4 wordPos = czm_inverseView * viewPos;
// 虚拟相机中坐标
vec4 vcPos = camera_view_matrix * wordPos;
float near = .001 * helsing_viewDistance;
float dis = length(vcPos.xyz);
if(dis > near && dis < helsing_viewDistance){
// 透视投影
vec4 posInEye = camera_projection_matrix * vcPos;
// 可视区颜色
// vec4 helsing_visibleAreaColor=vec4(0.,1.,0.,.5);
// vec4 helsing_invisibleAreaColor=vec4(1.,0.,0.,.5);
if(visible(posInEye)){
float vis = shadow(viewPos);
if(vis > 0.3){
out_FragColor = mix(out_FragColor,helsing_visibleAreaColor,.5);
} else{
out_FragColor = mix(out_FragColor,helsing_invisibleAreaColor,.5);
}
}
}
}`;

View File

@ -0,0 +1,773 @@
// ViewShed.js
import glsl from './glsl'
import glsl2 from './glsl2'
import Event from "../../../Event";
import MouseTip from "../../../MouseTip";
import Tools from "../../../Tools";
import EventBinding from '../../Element/Dialog/eventBinding';
import Controller from "../../../Controller";
import Dialog from '../../../BaseDialog';
import { html } from "./_element";
/**
* @constructor
* @description 可视域分析
* @param sdk
* @param {Object} options 选项。
* @param {Cesium.Cartesian3} options.viewPosition 观测点位置。
* @param {Cesium.Cartesian3} options.viewPositionEnd 最远观测点位置(如果设置了观测距离,这个属性可以不设置)。
* @param {Number} options.viewPointHeight=1.8 视点高度(单位`米`)。
* @param {Number} options.viewDistance 观测距离(单位`米`)。
* @param {Number} options.viewHeading 航向角(单位`度`)。
* @param {Number} options.viewPitch 俯仰角(单位`度`)。
* @param {Number} options.horizontalViewAngle=90 可视域水平夹角(单位`度`)。
* @param {Number} options.verticalViewAngle=60 可视域垂直夹角(单位`度`)。
* @param {String} options.visibleAreaColor=#008000 可视区域颜色(默认值`绿色`)。
* @param {String} options.invisibleAreaColor=#FF0000 不可视区域颜色(默认值`红色`)。
*/
class ViewShedStage extends Tools {
constructor(sdk, options = {}, _Dialog = {}) {
super(sdk, options)
// if (Object.hasOwn(options.viewPosition, 'lng') && Object.hasOwn(options.viewPosition, 'lat') && Object.hasOwn(options.viewPosition, 'alt')) {
// this.error = '请提供观测点位置!'
// window.ELEMENT && window.ELEMENT.Message({
// message: '请提供观测点位置!',
// type: 'warning',
// duration: 1500
// });
// return
// }
this.viewer = sdk.viewer;
this.options = {}
this.options.viewPosition = options.viewPosition;
this.options.viewPositionEnd = options.viewPositionEnd;
this.options.horizontalViewAngle = (options.horizontalViewAngle || options.horizontalViewAngle === 0) ? options.horizontalViewAngle : 90.0;
this.options.verticalViewAngle = (options.verticalViewAngle || options.verticalViewAngle === 0) ? options.verticalViewAngle : 60.0;
this.options.visibleAreaColor = options.visibleAreaColor || '#008000';
this.options.invisibleAreaColor = options.invisibleAreaColor || '#FF0000';
this._elms = {};
this.viewPointHeight = options.viewPointHeight
// this.enabled = (typeof options.enabled === "boolean") ? options.enabled : true;
// this.softShadows = (typeof options.softShadows === "boolean") ? options.softShadows : true;
// this.size = options.size || 10240; // 2048
this.ids = []
this.Dialog = _Dialog
this._EventBinding = new EventBinding()
this.html = null
YJ.Analysis.Analyses.push(this)
ViewShedStage.edit(this)
// ViewShedStage.edit(this)
// this.update();
}
get viewPointHeight() {
return this.options.viewPointHeight
}
set viewPointHeight(v) {
let viewPointHeight = Math.floor(Number(v) * 10) / 10
if (isNaN(viewPointHeight)) {
viewPointHeight = 1.8
}
if (viewPointHeight < 0) {
viewPointHeight = 0
}
this.options.viewPointHeight = viewPointHeight
this._elms.viewPointHeight && this._elms.viewPointHeight.forEach((item) => {
item.value = viewPointHeight
})
}
get viewPosition() {
return this.options.viewPosition
}
set viewPosition(v) {
this.options.viewPosition = v
this.ids[0] && (this.viewer.entities.getById(this.ids[0]).position = new Cesium.Cartesian3.fromDegrees(v.lng, v.lat, v.alt))
this.update()
// let viewPosition3 = Cesium.Cartesian3.fromDegrees(this.options.viewPosition.lng, this.options.viewPosition.lat, this.options.viewPosition.alt)
}
get viewPositionEnd() {
return this.options.viewPositionEnd
}
set viewPositionEnd(v) {
this.options.viewPositionEnd = v
this.ids[1] && (this.viewer.entities.getById(this.ids[1]).position = new Cesium.Cartesian3.fromDegrees(v.lng, v.lat, v.alt))
this.update()
// let viewPositionEnd3 = Cesium.Cartesian3.fromDegrees(this.options.viewPositionEnd.lng, this.options.viewPositionEnd.lat, this.options.viewPositionEnd.alt)
// this.viewDistance = this.viewPositionEnd ? Cesium.Cartesian3.distance(this.viewPosition, this.viewPositionEnd) : (options.viewDistance || 100.0);
}
get horizontalViewAngle() {
return this.options.horizontalViewAngle
}
set horizontalViewAngle(v) {
this.options.horizontalViewAngle = v
if (this._DialogObject && this._DialogObject._element && this._DialogObject._element.content) {
let contentElm = this._DialogObject._element.content
let e_horizontalViewAngle = contentElm.querySelector("input[name='horizontalViewAngle']")
e_horizontalViewAngle.value = v
let rangeNodeActive = contentElm.getElementsByClassName('range-node-active')[0]
let rangeNodeActiveText = rangeNodeActive.getElementsByClassName('range-node-active-text')[0]
rangeNodeActiveText.innerHTML = v + '°';
let rangeProcess = contentElm.getElementsByClassName('range-process')[0]
rangeProcess.style.width = v / 180 * 100 + '%'
}
this.update()
}
get visibleAreaColor() {
return this.options.visibleAreaColor
}
set visibleAreaColor(v) {
this.options.visibleAreaColor = v
this.update()
}
get invisibleAreaColor() {
return this.options.invisibleAreaColor
}
set invisibleAreaColor(v) {
this.options.invisibleAreaColor = v
this.update()
}
get verticalViewAngle() {
return this.options.verticalViewAngle
}
set verticalViewAngle(v) {
this.options.verticalViewAngle = v
this.update()
}
get viewDistance() {
let viewPosition3 = Cesium.Cartesian3.fromDegrees(this.options.viewPosition.lng, this.options.viewPosition.lat, this.options.viewPosition.alt + this.viewPointHeight)
let viewPositionEnd3 = Cesium.Cartesian3.fromDegrees(this.options.viewPositionEnd.lng, this.options.viewPositionEnd.lat, this.options.viewPositionEnd.alt)
let distance = Cesium.Cartesian3.distance(viewPosition3, viewPositionEnd3)
return distance
}
get viewHeading() {
let viewPosition3 = Cesium.Cartesian3.fromDegrees(this.options.viewPosition.lng, this.options.viewPosition.lat, this.options.viewPosition.alt + this.viewPointHeight)
let viewPositionEnd3 = Cesium.Cartesian3.fromDegrees(this.options.viewPositionEnd.lng, this.options.viewPositionEnd.lat, this.options.viewPositionEnd.alt)
let heading = getHeading(viewPosition3, viewPositionEnd3)
return heading
}
get viewPitch() {
let viewPosition3 = Cesium.Cartesian3.fromDegrees(this.options.viewPosition.lng, this.options.viewPosition.lat, this.options.viewPosition.alt + this.viewPointHeight)
let viewPositionEnd3 = Cesium.Cartesian3.fromDegrees(this.options.viewPositionEnd.lng, this.options.viewPositionEnd.lat, this.options.viewPositionEnd.alt)
let pitch = getPitch(viewPosition3, viewPositionEnd3)
return pitch
}
static create(that) {
that.destroy()
let count = 0;
if (!YJ.Measure.GetMeasureStatus()) {
that.event = new Event(that.sdk)
that.tip = new MouseTip('左键选择观测点位置,右键取消', that.sdk)
YJ.Measure.SetMeasureStatus(true)
that.event.mouse_left((movement, cartesian) => {
if (!that.viewPosition) {
that.options.viewPosition = that.cartesian3Towgs84(cartesian, that.viewer)
that.ids.push(ViewShedStage.create_point(that, cartesian))
that.tip.set_text("左键选择最远观测点位置,右键取消")
}
count++
if (count === 2) {
that.options.viewPositionEnd = that.cartesian3Towgs84(cartesian, that.viewer)
that.ids.push(ViewShedStage.create_point(that, cartesian))
end()
that.update()
}
})
that.event.mouse_move((movement, cartesian) => {
that.tip.setPosition(cartesian, movement.endPosition.x, movement.endPosition.y)
})
that.event.mouse_right((movement, cartesian) => {
that.ids.forEach(id => {
that.viewer.entities.removeById(id)
})
that.ids = []
end()
})
that.event.gesture_pinck_start((movement, cartesian) => {
let startTime = new Date()
that.event.gesture_pinck_end(() => {
let endTime = new Date()
if (endTime - startTime >= 500) {
that.ids.forEach(id => {
that.viewer.entities.removeById(id)
})
that.ids = []
end()
}
})
})
}
else {
console.log('上一次测量未结束')
}
function end() {
that.ids.forEach(id => {
let entity = that.viewer.entities.getById(id)
entity.show = false
})
YJ.Measure.SetMeasureStatus(false)
that.tip.destroy()
that.event.destroy()
that.tip = null
that.event = null
}
}
static create_point(that, cartesian) {
let id = that.randomString()
let p = that.cartesian3Towgs84(cartesian, that.viewer)
let params = {
id: id,
position: Cesium.Cartesian3.fromDegrees(p.lng, p.lat, p.alt),
billboard: {
image: that.getSourceRootPath() + '/img/point.png',
verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
disableDepthTestDistance: Number.POSITIVE_INFINITY,
color: Cesium.Color.WHITE.withAlpha(0.99)
}
}
that.viewer.entities.add(
new Cesium.Entity(params)
)
return id
}
add() {
this.createLightCamera();
this.createShadowMap();
this.createPostStage();
// this.drawFrustumOutline();
this.drawSketch();
ViewShedStage.getcanvas(this)
}
update() {
this.clear();
this.add();
}
static async edit(that) {
if (that._DialogObject && that._DialogObject.close) {
that._DialogObject.close()
that._DialogObject = null
}
that._DialogObject = await new Dialog(that.sdk.viewer._container, {
title: '可视域分析', left: '180px', top: '100px',
closeCallBack: () => {
that.Dialog.closeCallBack && that.Dialog.closeCallBack()
YJ.Measure.SetMeasureStatus(false)
that.editevent && that.editevent.destroy()
that.ControllerObject && that.ControllerObject.destroy()
that.ids.forEach(id => {
that.viewer.entities.removeById(id)
})
},
})
await that._DialogObject.init()
that._DialogObject._element.body.className = that._DialogObject._element.body.className + ' view-shed'
let contentElm = document.createElement('div');
contentElm.innerHTML = html()
that._DialogObject.contentAppChild(contentElm)
let resetBtn = that._DialogObject._element.body.getElementsByClassName('edit')[0];
resetBtn.addEventListener('click', () => {
that.nodeEdit()
})
let drawElm = document.createElement('button')
drawElm.innerHTML = '绘制'
drawElm.addEventListener('click', () => {
ViewShedStage.create(that)
})
that._DialogObject.footAppChild(drawElm)
that.html = contentElm
let all_elm = contentElm.getElementsByTagName("*")
that._EventBinding.on(that, all_elm)
that._elms = that._EventBinding.element
// //经度值
// let e_lng = contentElm.querySelector("span[name='lng']")
// e_lng.innerHTML = Number(that.options.viewPosition.lng.toFixed(8))
// //纬度值
// let e_lat = contentElm.querySelector("span[name='lat']")
// e_lat.innerHTML = Number(that.options.viewPosition.lat.toFixed(8))
// //高度值
// let e_alt = contentElm.querySelector("span[name='alt']")
// e_alt.innerHTML = Number(that.options.viewPosition.alt.toFixed(8))
// //偏航角
// let e_viewHeading = contentElm.querySelector("span[name='viewHeading']")
// e_viewHeading.innerHTML = Number(that.viewHeading.toFixed(8))
// //俯仰角
// let e_viewPitch = contentElm.querySelector("span[name='viewPitch']")
// e_viewPitch.innerHTML = Number(that.viewPitch.toFixed(8))
//视域夹角
let e_horizontalViewAngle = contentElm.querySelector("input[name='horizontalViewAngle']")
e_horizontalViewAngle.value = that.options.horizontalViewAngle
let rangeNodeActive = contentElm.getElementsByClassName('range-node-active')[0]
let rangeNodeActiveText = rangeNodeActive.getElementsByClassName('range-node-active-text')[0]
let rangeProcess = contentElm.getElementsByClassName('range-process')[0]
let percentage = that.horizontalViewAngle / 180 * 100
rangeNodeActive.style.left = percentage + '%';
rangeProcess.style.width = percentage + '%'
rangeNodeActiveText.innerHTML = that.horizontalViewAngle + '°';
let timeout
e_horizontalViewAngle.addEventListener('input', () => {
let percentage = e_horizontalViewAngle.value / 180 * 100
rangeNodeActive.style.left = percentage + '%';
rangeProcess.style.width = percentage + '%';
rangeNodeActiveText.innerHTML = e_horizontalViewAngle.value + '°';
})
e_horizontalViewAngle.addEventListener('change', () => {
clearTimeout(timeout)
timeout = setTimeout(() => {
that.horizontalViewAngle = e_horizontalViewAngle.value;
}, 300);
});
}
static getcanvas(that) {
if (!that.viewPosition) {
return
}
if (that.viewBillboardPrimitive) {
that.viewer.scene.primitives.remove(that.viewBillboardPrimitive)
that.viewBillboardPrimitive = null
}
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d')
canvas.width = 220
canvas.height = 180
canvas.style.background = "#000000"
let img = new Image();
const data = [
{
images: that.getSourceRootPath() + '/img/bubble/lng.png',
text: '经度:' + parseFloat(that.viewPosition.lng.toFixed(10)) + '°'
},
{
images: that.getSourceRootPath() + '/img/bubble/lat.png',
text: '纬度:' + parseFloat(that.viewPosition.lat.toFixed(10)) + '°'
},
{
images: that.getSourceRootPath() + '/img/bubble/h.png',
text: '高度:' + Number(((parseFloat(that.viewPosition.alt.toFixed(2)) + Number(that.viewPointHeight))).toFixed(2)) + ' m'
},
{
images: that.getSourceRootPath() + '/img/bubble/heading.png',
text: '偏航角:' + parseFloat(that.viewHeading.toFixed(10)) + '°'
},
{
images: that.getSourceRootPath() + '/img/bubble/pitch.png',
text: '俯仰角:' + parseFloat(that.viewPitch.toFixed(10)) + '°'
}
]
img.src = that.getSourceRootPath() + '/img/bubble/bubble.png';
let imagesLoaded = 0
img.onload = () => {
ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
data.forEach((item, index) => {
const img = new Image();
img.src = item.images;
img.onload = () => {
ctx.drawImage(img, 12, 12 + (index * 26));
ctx.fillStyle = "#fff";
ctx.font = "12px Arial";
ctx.fillText(item.text, 44, 28 + (index * 26));
imagesLoaded++;
if (imagesLoaded === data.length) {
that.viewBillboardPrimitive = that.viewer.scene.primitives.add(new Cesium.BillboardCollection())
that.viewBillboardPrimitive.add({
position: Cesium.Cartesian3.fromDegrees(that.viewPosition.lng, that.viewPosition.lat, that.viewPosition.alt + that.viewPointHeight),
image: canvas,
width: 200,
height: 180,
verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
disableDepthTestDistance: Number.POSITIVE_INFINITY,
})
}
};
})
};
}
clear() {
YJ.Measure.SetMeasureStatus(false)
this.tip && this.tip.destroy()
this.event && this.event.destroy()
this.tip = null
this.event = null
if (this.sketch) {
this.viewer.entities.removeById(this.sketch.id);
this.sketch = null;
}
if (this.frustumOutline) {
// this.frustumOutline.destroy();
this.viewer.entities.removeById(this.frustumOutline.id);
this.frustumOutline = null;
}
if (this.postStage) {
this.viewer.scene.postProcessStages.remove(this.postStage);
this.postStage = null;
}
}
destroy() {
this.clear()
this.editevent && this.editevent.destroy()
this.ControllerObject && this.ControllerObject.destroy()
this.ids.forEach(id => {
this.viewer.entities.removeById(id)
})
this.ids = []
if (this.viewBillboardPrimitive) {
this.viewer.scene.primitives.remove(this.viewBillboardPrimitive)
}
this.viewBillboardPrimitive = null
this.options.viewPosition = null
this.options.viewPositionEnd = null
YJ.Measure.SetMeasureStatus(false)
if (this._originalShadowMap) {
this.viewer.scene.shadowMap = this._originalShadowMap
this._originalShadowMap = null
}
this.viewer.shadows = this.viewer._shadows
}
nodeEdit() {
if (YJ.Measure.GetMeasureStatus()) {
console.log('上一次测量未结束')
}
else {
this.editevent && this.editevent.destroy()
this.ids.forEach(id => {
let entity = this.viewer.entities.getById(id)
entity.show = true
})
let selectPoint
YJ.Measure.SetMeasureStatus(true)
// this.tip = new MouseTip('左键选择要操作的观测点,右键取消', this.sdk)
this.editevent = new Event(this.sdk)
this.editevent.mouse_left((movement, cartesian) => {
let pick = this.viewer.scene.pick(movement.position);
if (pick && pick.id && pick.id.id && this.ids.indexOf(pick.id.id) != -1 && (!selectPoint || selectPoint.id != pick.id.id)) {
selectPoint = pick.id
// this.event.destroy()
// this.tip.destroy()
this.viewer.entities.getById(this.ids[0]).position = new Cesium.Cartesian3.fromDegrees(this.viewPosition.lng, this.viewPosition.lat, this.viewPosition.alt)
this.viewer.entities.getById(this.ids[1]).position = new Cesium.Cartesian3.fromDegrees(this.viewPositionEnd.lng, this.viewPositionEnd.lat, this.viewPositionEnd.alt)
this.ControllerObject && this.ControllerObject.destroy()
this.ControllerObject = new Controller(this.sdk, { position: { ...this.cartesian3Towgs84(selectPoint.position._value, this.sdk.viewer) } })
this.ControllerObject.controllerCallBack = (params, status) => {
if (params.position.alt < 0) {
params.position.alt = 0
}
selectPoint.position = new Cesium.Cartesian3.fromDegrees(params.position.lng, params.position.lat, params.position.alt)
if (status) {
if (this.ids.indexOf(pick.id.id) == 0) {
this.viewPosition = params.position
}
else {
this.viewPositionEnd = params.position
}
YJ.Measure.SetMeasureStatus(true)
}
}
this.ControllerObject.editTranslational()
}
})
this.editevent.mouse_right((movement, cartesian) => {
YJ.Measure.SetMeasureStatus(false)
this.editevent && this.editevent.destroy()
this.ControllerObject && this.ControllerObject.destroy()
this.ids.forEach(id => {
let entity = this.viewer.entities.getById(id)
entity.show = false
})
selectPoint = null
})
this.editevent.mouse_move((movement, cartesian) => {
// this.tip.setPosition(
// cartesian,
// movement.endPosition.x,
// movement.endPosition.y
// )
})
this.editevent.gesture_pinck_start((movement, cartesian) => {
let startTime = new Date()
this.editevent.gesture_pinck_end(() => {
let endTime = new Date()
if (endTime - startTime >= 500) {
YJ.Measure.SetMeasureStatus(false)
this.editevent && this.editevent.destroy()
this.ControllerObject && this.ControllerObject.destroy()
this.ids.forEach(id => {
let entity = this.viewer.entities.getById(id)
entity.show = false
})
selectPoint = null
}
})
})
}
}
createLightCamera() {
if (!this.options.viewPosition) {
return
}
let _this = this
this.lightCamera = new Cesium.Camera(this.viewer.scene);
this.lightCamera.position = Cesium.Cartesian3.fromDegrees(this.options.viewPosition.lng, this.options.viewPosition.lat, this.options.viewPosition.alt + this.viewPointHeight);
// if (this.viewPositionEnd) {
// let direction = Cesium.Cartesian3.normalize(Cesium.Cartesian3.subtract(this.viewPositionEnd, this.viewPosition, new Cesium.Cartesian3()), new Cesium.Cartesian3());
// this.lightCamera.direction = direction; // direction是相机面向的方向
// }
this.lightCamera.frustum.near = this.viewDistance * 0.001;
this.lightCamera.frustum.far = this.viewDistance;
const hr = Cesium.Math.toRadians(this.horizontalViewAngle);
const vr = Cesium.Math.toRadians(this.verticalViewAngle);
const aspectRatio =
(this.viewDistance * Math.tan(hr / 2) * 2) /
(this.viewDistance * Math.tan(vr / 2) * 2);
this.lightCamera.frustum.aspectRatio = aspectRatio;
if (hr > vr) {
this.lightCamera.frustum.fov = hr;
} else {
this.lightCamera.frustum.fov = vr;
}
this.lightCamera.setView({
destination: Cesium.Cartesian3.fromDegrees(this.options.viewPosition.lng, this.options.viewPosition.lat, this.options.viewPosition.alt + this.viewPointHeight),
orientation: {
heading: Cesium.Math.toRadians(this.viewHeading || 0),
pitch: Cesium.Math.toRadians(this.viewPitch || 0),
roll: 0
}
});
}
createShadowMap() {
this.shadowMap = new Cesium.ShadowMap({
context: (this.viewer.scene).context,
lightCamera: this.lightCamera,
enabled: true,
isPointLight: true,
pointLightRadius: this.viewDistance,
cascadesEnabled: false,
size: 2048, // 2048
softShadows: true,
normalOffset: false,
fromLightSource: false
});
if (!this._originalShadowMap) {
this._originalShadowMap = this.viewer.scene.shadowMap
}
this.viewer.scene.shadowMap = this.shadowMap;
// setTimeout(() => {
// this.viewer.shadows = this.viewer._shadows
// }, 0);
}
createPostStage() {
const fs = glsl
if (Number(Cesium.VERSION.split('.')[1]) >= 102) {
fs = glsl2
}
const postStage = new Cesium.PostProcessStage({
fragmentShader: fs,
uniforms: {
shadowMap_textureCube: () => {
this.shadowMap.update(Reflect.get(this.viewer.scene, "_frameState"));
return Reflect.get(this.shadowMap, "_shadowMapTexture");
},
shadowMap_matrix: () => {
this.shadowMap.update(Reflect.get(this.viewer.scene, "_frameState"));
return Reflect.get(this.shadowMap, "_shadowMapMatrix");
},
shadowMap_lightPositionEC: () => {
this.shadowMap.update(Reflect.get(this.viewer.scene, "_frameState"));
return Reflect.get(this.shadowMap, "_lightPositionEC");
},
shadowMap_normalOffsetScaleDistanceMaxDistanceAndDarkness: () => {
this.shadowMap.update(Reflect.get(this.viewer.scene, "_frameState"));
const bias = this.shadowMap._pointBias;
return Cesium.Cartesian4.fromElements(
bias.normalOffsetScale,
this.shadowMap._distance,
this.shadowMap.maximumDistance,
0.0,
new Cesium.Cartesian4()
);
},
shadowMap_texelSizeDepthBiasAndNormalShadingSmooth: () => {
this.shadowMap.update(Reflect.get(this.viewer.scene, "_frameState"));
const bias = this.shadowMap._pointBias;
const scratchTexelStepSize = new Cesium.Cartesian2();
const texelStepSize = scratchTexelStepSize;
texelStepSize.x = 1.0 / this.shadowMap._textureSize.x;
texelStepSize.y = 1.0 / this.shadowMap._textureSize.y;
return Cesium.Cartesian4.fromElements(
texelStepSize.x,
texelStepSize.y,
bias.depthBias,
bias.normalShadingSmooth,
new Cesium.Cartesian4()
);
},
camera_projection_matrix: this.lightCamera.frustum.projectionMatrix,
camera_view_matrix: this.lightCamera.viewMatrix,
helsing_viewDistance: () => {
return this.viewDistance;
},
helsing_visibleAreaColor: Cesium.Color.fromCssColorString(this.visibleAreaColor),
helsing_invisibleAreaColor: Cesium.Color.fromCssColorString(this.invisibleAreaColor),
}
});
this.postStage = this.viewer.scene.postProcessStages.add(postStage);
}
drawFrustumOutline() {
const scratchRight = new Cesium.Cartesian3();
const scratchRotation = new Cesium.Matrix3();
const scratchOrientation = new Cesium.Quaternion();
const position = this.lightCamera.positionWC;
const direction = this.lightCamera.directionWC;
const up = this.lightCamera.upWC;
let right = this.lightCamera.rightWC;
right = Cesium.Cartesian3.negate(right, scratchRight);
let rotation = scratchRotation;
Cesium.Matrix3.setColumn(rotation, 0, right, rotation);
Cesium.Matrix3.setColumn(rotation, 1, up, rotation);
Cesium.Matrix3.setColumn(rotation, 2, direction, rotation);
let orientation = Cesium.Quaternion.fromRotationMatrix(rotation, scratchOrientation);
let instance = new Cesium.GeometryInstance({
geometry: new Cesium.FrustumOutlineGeometry({
frustum: this.lightCamera.frustum,
origin: Cesium.Cartesian3.fromDegrees(this.options.viewPosition.lng, this.options.viewPosition.lat, this.options.viewPosition.alt + this.viewPointHeight),
orientation: orientation
}),
id: Math.random().toString(36).substr(2),
attributes: {
color: Cesium.ColorGeometryInstanceAttribute.fromColor(
Cesium.Color.YELLOWGREEN//new Cesium.Color(0.0, 1.0, 0.0, 1.0)
),
show: new Cesium.ShowGeometryInstanceAttribute(true)
}
});
this.frustumOutline = this.viewer.scene.primitives.add(
new Cesium.Primitive({
geometryInstances: [instance],
appearance: new Cesium.PerInstanceColorAppearance({
flat: true,
translucent: false
})
})
);
}
drawSketch() {
this.sketch = this.viewer.entities.add({
name: 'sketch',
position: Cesium.Cartesian3.fromDegrees(this.options.viewPosition.lng, this.options.viewPosition.lat, this.options.viewPosition.alt + this.viewPointHeight),
orientation: Cesium.Transforms.headingPitchRollQuaternion(
Cesium.Cartesian3.fromDegrees(this.options.viewPosition.lng, this.options.viewPosition.lat, this.options.viewPosition.alt + this.viewPointHeight),
Cesium.HeadingPitchRoll.fromDegrees(this.viewHeading - 90, this.viewPitch, 0.0)
),
ellipsoid: {
radii: new Cesium.Cartesian3(
this.viewDistance,
this.viewDistance,
this.viewDistance
),
// innerRadii: new Cesium.Cartesian3(2.0, 2.0, 2.0),
minimumClock: Cesium.Math.toRadians(-this.horizontalViewAngle / 2),
maximumClock: Cesium.Math.toRadians(this.horizontalViewAngle / 2),
minimumCone: Cesium.Math.toRadians(90 - (this.verticalViewAngle / 2)),
maximumCone: Cesium.Math.toRadians(90 + (this.verticalViewAngle / 2)),
fill: false,
outline: true,
subdivisions: 256,
stackPartitions: 64,
slicePartitions: 64,
outlineColor: Cesium.Color.YELLOWGREEN
}
});
this.frustumOutline = this.viewer.entities.add({
name: 'sketch',
position: Cesium.Cartesian3.fromDegrees(this.options.viewPosition.lng, this.options.viewPosition.lat, this.options.viewPosition.alt + this.viewPointHeight),
orientation: Cesium.Transforms.headingPitchRollQuaternion(
Cesium.Cartesian3.fromDegrees(this.options.viewPosition.lng, this.options.viewPosition.lat, this.options.viewPosition.alt + this.viewPointHeight),
Cesium.HeadingPitchRoll.fromDegrees(this.viewHeading - 90, this.viewPitch, 0.0)
),
ellipsoid: {
radii: new Cesium.Cartesian3(
this.viewDistance,
this.viewDistance,
this.viewDistance
),
innerRadii: new Cesium.Cartesian3(0.0001, 0.0001, 0.0001),
minimumClock: Cesium.Math.toRadians(-this.horizontalViewAngle / 2),
maximumClock: Cesium.Math.toRadians(this.horizontalViewAngle / 2),
minimumCone: Cesium.Math.toRadians(90 - (this.verticalViewAngle / 2)),
maximumCone: Cesium.Math.toRadians(90 + (this.verticalViewAngle / 2)),
fill: false,
outline: true,
subdivisions: 256,
stackPartitions: 1,
slicePartitions: 1,
outlineColor: Cesium.Color.YELLOWGREEN
}
});
}
}
function getHeading(fromPosition, toPosition) {
let finalPosition = new Cesium.Cartesian3();
let matrix4 = Cesium.Transforms.eastNorthUpToFixedFrame(fromPosition);
Cesium.Matrix4.inverse(matrix4, matrix4);
Cesium.Matrix4.multiplyByPoint(matrix4, toPosition, finalPosition);
Cesium.Cartesian3.normalize(finalPosition, finalPosition);
return Cesium.Math.toDegrees(Math.atan2(finalPosition.x, finalPosition.y));
}
function getPitch(fromPosition, toPosition) {
let finalPosition = new Cesium.Cartesian3();
let matrix4 = Cesium.Transforms.eastNorthUpToFixedFrame(fromPosition);
Cesium.Matrix4.inverse(matrix4, matrix4);
Cesium.Matrix4.multiplyByPoint(matrix4, toPosition, finalPosition);
Cesium.Cartesian3.normalize(finalPosition, finalPosition);
return Cesium.Math.toDegrees(Math.asin(finalPosition.z));
}
export default ViewShedStage;

View File

@ -0,0 +1,74 @@
function html() {
return `
<span class="custom-divider"></span>
<div class="div-item">
<div class="row">
<div class="col">
<span class="label">位置拾取(起点、终点)</span>
<button class="edit"><svg class="icon-edit"><use xlink:href="#yj-icon-edit"></use></svg>拾取</button>
</div>
</div>
</div>
<span class="custom-divider"></span>
<div class="div-item">
<div class="row subtitle-box">
<span class="subtitle">视域夹角</span>
</div>
<div class="row">
<div class="col">
<div class="range-box">
<div class="range-bg">
<div class="range-process-box">
<div class="range-process"></div>
</div>
</div>
<div class="range-node-box">
<span class="range-node-text">0°</span>
<span class="range-node-text">45°</span>
<span class="range-node-text">90°</span>
<span class="range-node-text">135°</span>
<span class="range-node-text">180°</span>
<div class="range-node-active"><span class="range-node-active-text">0°</span></div>
</div>
<input type="range" max="180" min="0" step="1" name="horizontalViewAngle">
</div>
</div>
</div>
</div>
<span class="custom-divider"></span>
<div class="div-item">
<div class="row">
<div class="col">
<span class="label">经度:</span>
<span class="text-number" name="lng"></span>
</div>
<div class="col">
<span class="label">偏航角:</span>
<span class="text-number" name="viewHeading"></span>
<span class="unit">°</span>
</div>
</div>
<div class="row">
<div class="col">
<span class="label">纬度:</span>
<span class="text-number" name="lat"></span>
</div>
<div class="col">
<span class="label">俯仰角:</span>
<span class="text-number" name="viewPitch"></span>
<span class="unit">°</span>
</div>
</div>
<div class="row">
<div class="col">
<span class="label">高度:</span>
<span class="text-number" name="alt"></span>
<span class="unit text-number" style="margin-left: 5px;">m</span>
</div>
</div>
</div>
<span class="custom-divider"></span>
`
}
export { html }

View File

@ -0,0 +1,512 @@
/*
* @Author: Wang jianLei
* @Date: 2022-05-17 21:49:28
* @Last Modified by: Wang JianLei
* @Last Modified time: 2022-05-19 22:08:14
*/
let ViewShed = function (sdk, canvasEleId) {
if (!sdk.viewer) throw new Error("no viewer object!");
alert(canvasEleId)
let canvasEle = document.getElementById(canvasEleId);
if (!canvasEle) throw new Error("the canvas element is not exist");
this.canvasEle = canvasEle;
this.viewer = sdk.viewer;
this.handler = undefined;
this.lightCamera;
this.pyramid;
this.frustumPrimitive;
this.viewershedPolygon;
};
ViewShed.prototype = {
/**
* 初始化handler
*/
initHandler() {
if (this.handler) {
this.handler.destroy();
this.handler = undefined;
}
},
/**
* 开始执行视域分析
* @param {number} precision 精度值越大创建耗时越长建议在10~20之间
*/
createViewshed: function (precision) {
let $this = this;
let scene = $this.viewer.scene;
$this.initHandler();
$this.clearAll();
$this.handler = new Cesium.ScreenSpaceEventHandler($this.viewer.canvas);
$this.handler.setInputAction((event) => {
// 禁止地球旋转和缩放,地球的旋转会对鼠标移动监听有影响,所以需要禁止
scene.screenSpaceCameraController.enableRotate = false;
scene.screenSpaceCameraController.enableZoom = false;
scene.globe.depthTestAgainstTerrain = true;
let earthPosition = scene.pickPosition(event.position);
let pos = $this.cartesian3ToDegree(earthPosition);
$this.handler.setInputAction(function (event) {
let newPosition = scene.pickPosition(event.endPosition);
if (Cesium.defined(newPosition)) {
let pos1 = $this.cartesian3ToDegree(newPosition);
let distance = Cesium.Cartesian3.distance(newPosition, earthPosition);
let angle = $this.getAngle(pos[0], pos[1], pos1[0], pos1[1]);
let pitch = $this.getPitch(earthPosition, newPosition);
$this.ViewShedOptions = {
viewPosition: earthPosition, //观测点 笛卡尔坐标
endPosition: newPosition, //目标点 笛卡尔坐标
direction: angle, //观测方位角 默认为`0`,范围`0~360`
pitch: pitch, //俯仰角,radius,默认为`0`
horizontalViewAngle: 90, //可视域水平夹角,默认为 `90`,范围`0~360`
verticalViewAngle: 60, //可视域垂直夹角,默认为`60`,范围`0~180`
visibleAreaColor: Cesium.Color.GREEN, //可视区域颜色,默认为`green`
invisibleAreaColor: Cesium.Color.RED, //不可见区域颜色,默认为`red`
visualRange: distance, //距离,单位`米`
};
$this.updateViewShed();
}
}, Cesium.ScreenSpaceEventType.MOUSE_MOVE);
}, Cesium.ScreenSpaceEventType.LEFT_DOWN);
$this.handler.setInputAction(() => {
$this.initHandler();
// 开启地球旋转和缩放
scene.screenSpaceCameraController.enableRotate = true;
scene.screenSpaceCameraController.enableZoom = true;
$this.drawViewershed(precision);
}, Cesium.ScreenSpaceEventType.LEFT_UP);
},
ReturnDistance(pos0, pos1) {
let distance = 0;
let point1cartographic = Cesium.Cartographic.fromCartesian(pos0);
let point2cartographic = Cesium.Cartographic.fromCartesian(pos1);
/**根据经纬度计算出距离**/
let geodesic = new Cesium.EllipsoidGeodesic();
geodesic.setEndPoints(point1cartographic, point2cartographic);
let s = geodesic.surfaceDistance;
return s;
},
getHeight(x, y, objectsToExclude) {
let endCartographic = Cesium.Cartographic.fromDegrees(x, y);
let endHeight = this.viewer.scene.sampleHeight(
endCartographic,
objectsToExclude
);
return endHeight;
},
cartesian3ToDegree: function (Cartesian3) {
let _ellipsoid = this.viewer.scene.globe.ellipsoid;
let _cartographic = _ellipsoid.cartesianToCartographic(Cartesian3);
let _lat = Cesium.Math.toDegrees(_cartographic.latitude);
let _lng = Cesium.Math.toDegrees(_cartographic.longitude);
let _alt = _cartographic.height;
return [_lng, _lat, _alt];
},
getAngle: function (lng1, lat1, lng2, lat2) {
let dRotateAngle = Math.atan2(Math.abs(lng1 - lng2), Math.abs(lat1 - lat2));
if (lng2 >= lng1) {
dRotateAngle = lat2 < lat1 ? Math.PI - dRotateAngle : dRotateAngle;
} else {
dRotateAngle =
lat2 >= lat1 ? 2 * Math.PI - dRotateAngle : Math.PI + dRotateAngle;
}
dRotateAngle = (dRotateAngle * 180) / Math.PI;
return dRotateAngle;
},
getPitch(pointA, pointB) {
let transfrom = Cesium.Transforms.eastNorthUpToFixedFrame(pointA);
const vector = Cesium.Cartesian3.subtract(
pointB,
pointA,
new Cesium.Cartesian3()
);
let direction = Cesium.Matrix4.multiplyByPointAsVector(
Cesium.Matrix4.inverse(transfrom, transfrom),
vector,
vector
);
Cesium.Cartesian3.normalize(direction, direction);
return Cesium.Math.PI_OVER_TWO - Cesium.Math.acosClamped(direction.z);
},
updateViewShed: function () {
this.clear();
this.setLightCamera();
this.addVisualPyramid();
this.createFrustum();
},
clear: function () {
if (this.pyramid) {
this.viewer.entities.removeById(this.pyramid.id);
this.pyramid = undefined;
}
if (this.frustumPrimitive) {
this.viewer.scene.primitives.remove(this.frustumPrimitive);
this.frustumPrimitive = undefined;
}
if (this.debugModelMatrixPrimitive) {
this.viewer.scene.primitives.remove(this.debugModelMatrixPrimitive);
this.debugModelMatrixPrimitive = undefined;
}
},
clearAll: function () {
this.clear();
if (this.viewershedPolygon) {
this.viewer.scene.primitives.remove(this.viewershedPolygon);
this.viewershedPolygon = undefined;
}
},
addVisualPyramid: function () {
let options = this.ViewShedOptions;
let position = options.viewPosition;
let visualRange = Number(options.visualRange);
let transform = Cesium.Transforms.eastNorthUpToFixedFrame(position);
this.debugModelMatrixPrimitive = this.viewer.scene.primitives.add(
new Cesium.DebugModelMatrixPrimitive({
modelMatrix: transform,
length: 5.0,
})
);
const halfClock = options.horizontalViewAngle / 2;
const halfCone = options.verticalViewAngle / 2;
const pitch = Cesium.Math.toDegrees(options.pitch);
const ellipsoid = new Cesium.EllipsoidGraphics({
radii: new Cesium.Cartesian3(visualRange, visualRange, visualRange),
minimumClock: Cesium.Math.toRadians(90 - options.direction - halfClock),
maximumClock: Cesium.Math.toRadians(90 - options.direction + halfClock),
minimumCone: Cesium.Math.toRadians(90 - pitch - halfCone),
maximumCone: Cesium.Math.toRadians(90 - pitch + halfCone),
fill: false,
outline: true,
subdivisions: 256,
stackPartitions: 64,
slicePartitions: 64,
outlineColor: Cesium.Color.YELLOWGREEN.withAlpha(0.5),
});
const pyramidEntity = new Cesium.Entity({
position: position,
ellipsoid,
});
this.pyramid = this.viewer.entities.add(pyramidEntity);
},
setLightCamera: function () {
if (!this.lightCamera) {
this.lightCamera = new Cesium.Camera(this.viewer.scene);
}
let options = this.ViewShedOptions;
let visualRange = Number(options.visualRange);
this.lightCamera.position = options.viewPosition;
this.lightCamera.frustum.near = 0.1;
this.lightCamera.frustum.far = visualRange;
const hr = Cesium.Math.toRadians(options.horizontalViewAngle);
const vr = Cesium.Math.toRadians(options.verticalViewAngle);
this.lightCamera.frustum.aspectRatio =
(visualRange * Math.tan(hr / 2) * 2) /
(visualRange * Math.tan(vr / 2) * 2);
this.lightCamera.frustum.fov = hr > vr ? hr : vr;
this.lightCamera.setView({
destination: options.viewPosition,
orientation: {
heading: Cesium.Math.toRadians(options.direction || 0),
pitch: options.pitch || 0,
roll: 0,
},
});
},
createFrustum: function () {
const scratchRight = new Cesium.Cartesian3();
const scratchRotation = new Cesium.Matrix3();
const scratchOrientation = new Cesium.Quaternion();
const direction = this.lightCamera.directionWC;
const up = this.lightCamera.upWC;
let right = this.lightCamera.rightWC;
right = Cesium.Cartesian3.negate(right, scratchRight);
let rotation = scratchRotation;
Cesium.Matrix3.setColumn(rotation, 0, right, rotation);
Cesium.Matrix3.setColumn(rotation, 1, up, rotation);
Cesium.Matrix3.setColumn(rotation, 2, direction, rotation);
let orientation = Cesium.Quaternion.fromRotationMatrix(
rotation,
scratchOrientation
);
let instanceOutline = new Cesium.GeometryInstance({
geometry: new Cesium.FrustumOutlineGeometry({
frustum: this.lightCamera.frustum,
origin: this.ViewShedOptions.viewPosition,
orientation: orientation,
}),
id: "视椎体轮廓线" + Math.random().toString(36).substr(2),
attributes: {
color: Cesium.ColorGeometryInstanceAttribute.fromColor(
new Cesium.Color(0.0, 1.0, 0.0, 1.0)
),
show: new Cesium.ShowGeometryInstanceAttribute(true),
},
});
this.frustumPrimitive = this.viewer.scene.primitives.add(
new Cesium.Primitive({
geometryInstances: instanceOutline,
appearance: new Cesium.PerInstanceColorAppearance({
flat: true,
translucent: false,
closed: true,
}),
})
);
},
createPoint: function (firstPos, secondPos) {
let entity4FirstPos = new Cesium.Entity({
name: "firstPos",
show: true,
position: firstPos,
point: {
show: true,
pixelSize: 20,
color: Cesium.Color.RED,
outlineColor: Cesium.Color.YELLOW,
outlineWidth: 5,
},
description: `
<p>这是绘制的视椎体起点</p>`,
});
this.viewer.entities.add(entity4FirstPos);
let entity4SecondPos = new Cesium.Entity({
name: "secondPos",
show: true,
position: secondPos,
point: {
show: true,
pixelSize: 30,
color: Cesium.Color.YELLOW,
outlineColor: Cesium.Color.RED,
outlineWidth: 8,
},
description: `
<p>这是绘制的视椎体视角终点</p>`,
});
this.viewer.entities.add(entity4SecondPos);
},
//绘制可视域
add(positionArr) {
let polygon = new Cesium.PolygonGeometry({
polygonHierarchy: new Cesium.PolygonHierarchy(
Cesium.Cartesian3.fromDegreesArray(positionArr)
),
height: 0.0,
extrudedHeight: 0.0,
vertexFormat: Cesium.PerInstanceColorAppearance.VERTEX_FORMAT,
stRotation: 0.0, // 纹理的旋转坐标(以弧度为单位),正旋转是逆时针方向
ellipsoid: Cesium.Ellipsoid.WGS84,
granularity: Cesium.Math.RADIANS_PER_DEGREE, // 每个纬度和经度之间的距离(以弧度为单位),确定缓冲区中的位置数
perPositionHeight: false, // 每个位置点使用的高度
closeTop: true,
closeBottom: true,
// NONE 与椭圆表面不符的直线;GEODESIC 遵循测地路径;RHUMB 遵循大黄蜂或恶魔般的道路。
arcType: Cesium.ArcType.GEODESIC, // 多边形边缘线型
});
let polygonInstance = new Cesium.GeometryInstance({
geometry: polygon,
name: "ViewershedPolygon",
attributes: {
color: Cesium.ColorGeometryInstanceAttribute.fromColor(
Cesium.Color.BLUE.withAlpha(0.6)
),
show: new Cesium.ShowGeometryInstanceAttribute(true), //显示或者隐藏
},
});
this.viewershedPolygon = this.viewer.scene.primitives.add(
new Cesium.GroundPrimitive({
geometryInstances: polygonInstance,
appearance: new Cesium.EllipsoidSurfaceAppearance({
aboveGround: true,
material: new Cesium.Material({
fabric: {
type: "Image",
uniforms: {
image: this.returnImgae(),
},
},
}),
}),
})
);
},
drawViewershed(precision) {
const pos = this.cartesian3ToDegree(this.ViewShedOptions.viewPosition);
const radius = this.ViewShedOptions.visualRange;
const direction = this.ViewShedOptions.direction;
let boundary = this.computeBoundaryOptions(pos, radius, direction);
const bbox = boundary.bbox;
let mask = turf.polygon([boundary.boundaryPoints]);
const dis = this.ViewShedOptions.visualRange / (precision * 1000);
let gridPoints = turf.pointGrid(bbox, dis, { mask: mask });
let pointsResult = this.createTargetPoints(gridPoints, dis, pos);
let variogram = kriging.train(
pointsResult.values,
pointsResult.lngs,
pointsResult.lats,
"exponential",
0,
100
);
let grid = kriging.grid([boundary.boundaryPoints], variogram, dis / 1000);
const colors = [
"#ff000080",
"#ff000080",
"#ff000080",
"#ff000080",
"#ff000080",
"#ff000080",
"#00ff0080",
"#00ff0080",
"#00ff0080",
"#00ff0080",
"#00ff0080",
"#00ff0080",
];
this.canvasEle.width = 3840;
this.canvasEle.height = 2160;
kriging.plot(
this.canvasEle,
grid,
[bbox[0], bbox[2]],
[bbox[1], bbox[3]],
colors
);
this.add(boundary.positionArr);
},
computeBoundaryOptions(pos, radius, angle) {
let Ea = 6378137; // 赤道半径
let Eb = 6356725; // 极半径
const lng = pos[0],
lat = pos[1];
const bbox = [lng, lat, lng, lat]; //[minX, minY, maxX, maxY]
let positionArr = [];
let boundaryPoints = [];
positionArr.push(lng, lat);
boundaryPoints.push([lng, lat]);
//正北是0°
let start = angle + 45 > 360 ? angle - 45 - 360 : angle - 45;
let end = start + 90;
for (let i = start; i <= end; i++) {
let dx = radius * Math.sin((i * Math.PI) / 180.0);
let dy = radius * Math.cos((i * Math.PI) / 180.0);
let ec = Eb + ((Ea - Eb) * (90.0 - lat)) / 90.0;
let ed = ec * Math.cos((lat * Math.PI) / 180);
let BJD = lng + ((dx / ed) * 180.0) / Math.PI;
let BWD = lat + ((dy / ec) * 180.0) / Math.PI;
positionArr.push(BJD, BWD);
boundaryPoints.push([BJD, BWD]);
this.refreshBBox(bbox, BJD, BWD);
}
boundaryPoints.push([lng, lat]);
return {
positionArr,
boundaryPoints,
bbox,
};
},
/**
* 更新外围矩形 Bbox
* @param {Array} result 外围矩形Bbox-[minX, minY, maxX, maxY]
* @param {Number} x 经度
* @param {Number} y 纬度
*/
refreshBBox(result, x, y) {
result[0] = x < result[0] ? x : result[0];
result[1] = y < result[1] ? y : result[1];
result[2] = x > result[2] ? x : result[2];
result[3] = y > result[3] ? y : result[3];
},
/**
* 插值点用射线判断通视性
* @param {*} gridPoints 网格点
* @param {*} step 步长,可以理解成是精度
* @param {*} sourcePos 视域分析起点
* @returns kriging插值所需的参数对象{ values:[], lngs:[], lats:[]}
*/
createTargetPoints(gridPoints, step, sourcePos) {
let positionArr = [];
let objectsToExclude = [
this.frustumPrimitive,
this.pyramid,
this.debugModelMatrixPrimitive,
];
let values = [],
lngs = [],
lats = [];
let height = this.getHeight(sourcePos[0], sourcePos[1], objectsToExclude);
positionArr.push({
x: sourcePos[0],
y: sourcePos[1],
z: height,
});
let viewPoint = this.ViewShedOptions.viewPosition;
for (let index = 0; index < gridPoints.features.length; index++) {
const feature = gridPoints.features[index];
const coords = feature.geometry.coordinates;
const x = coords[0],
y = coords[1];
let h = this.getHeight(x, y, objectsToExclude);
let endPoint = Cesium.Cartesian3.fromDegrees(x, y, h);
let direction = Cesium.Cartesian3.normalize(
Cesium.Cartesian3.subtract(
endPoint,
viewPoint,
new Cesium.Cartesian3()
),
new Cesium.Cartesian3()
);
// 建立射线
let ray = new Cesium.Ray(viewPoint, direction);
let result = this.viewer.scene.pickFromRay(ray, objectsToExclude); // 计算交互点,返回第一个
if (result) {
let buffer = this.ReturnDistance(endPoint, result.position);
// let M_color = Cesium.Color.GREEN;
if (buffer > step) {
// M_color = Cesium.Color.RED;
values.push(0);
} else {
values.push(1);
}
lngs.push(x);
lats.push(y);
// this.viewer.entities.add(
// new Cesium.Entity({
// name: "插值点哦",
// show: true,
// position: endPoint,
// point: {
// show: true,
// pixelSize: 10,
// color: M_color,
// outlineWidth: 2,
// outlineColor: Cesium.Color.YELLOW,
// },
// })
// );
}
}
return {
values,
lngs,
lats,
};
},
/**
* canvas转image图片
* @returns base64图片
*/
returnImgae() {
return this.canvasEle.toDataURL("image/png");
},
};
export default ViewShed;

View File

@ -0,0 +1,131 @@
export default `
#define USE_CUBE_MAP_SHADOW true
uniform sampler2D colorTexture;
uniform sampler2D depthTexture;
varying vec2 v_textureCoordinates;
uniform mat4 camera_projection_matrix;
uniform mat4 camera_view_matrix;
uniform samplerCube shadowMap_textureCube;
uniform mat4 shadowMap_matrix;
uniform vec4 shadowMap_lightPositionEC;
uniform vec4 shadowMap_normalOffsetScaleDistanceMaxDistanceAndDarkness;
uniform vec4 shadowMap_texelSizeDepthBiasAndNormalShadingSmooth;
uniform float helsing_viewDistance;
uniform vec4 helsing_visibleAreaColor;
uniform vec4 helsing_invisibleAreaColor;
struct zx_shadowParameters
{
vec3 texCoords;
float depthBias;
float depth;
float nDotL;
vec2 texelStepSize;
float normalShadingSmooth;
float darkness;
};
float czm_shadowVisibility(samplerCube shadowMap, zx_shadowParameters shadowParameters)
{
float depthBias = shadowParameters.depthBias;
float depth = shadowParameters.depth;
float nDotL = shadowParameters.nDotL;
float normalShadingSmooth = shadowParameters.normalShadingSmooth;
float darkness = shadowParameters.darkness;
vec3 uvw = shadowParameters.texCoords;
depth -= depthBias;
float visibility = czm_shadowDepthCompare(shadowMap, uvw, depth);
return czm_private_shadowVisibility(visibility, nDotL, normalShadingSmooth, darkness);
}
vec4 getPositionEC(){
return czm_windowToEyeCoordinates(gl_FragCoord);
}
vec3 getNormalEC(){
return vec3(1.);
}
vec4 toEye(in vec2 uv,in float depth){
vec2 xy=vec2((uv.x*2.-1.),(uv.y*2.-1.));
vec4 posInCamera=czm_inverseProjection*vec4(xy,depth,1.);
posInCamera=posInCamera/posInCamera.w;
return posInCamera;
}
vec3 pointProjectOnPlane(in vec3 planeNormal,in vec3 planeOrigin,in vec3 point){
vec3 v01=point-planeOrigin;
float d=dot(planeNormal,v01);
return(point-planeNormal*d);
}
float getDepth(in vec4 depth){
float z_window=czm_unpackDepth(depth);
z_window=czm_reverseLogDepth(z_window);
float n_range=czm_depthRange.near;
float f_range=czm_depthRange.far;
return(2.*z_window-n_range-f_range)/(f_range-n_range);
}
float shadow(in vec4 positionEC){
vec3 normalEC=getNormalEC();
zx_shadowParameters shadowParameters;
shadowParameters.texelStepSize=shadowMap_texelSizeDepthBiasAndNormalShadingSmooth.xy;
shadowParameters.depthBias=shadowMap_texelSizeDepthBiasAndNormalShadingSmooth.z;
shadowParameters.normalShadingSmooth=shadowMap_texelSizeDepthBiasAndNormalShadingSmooth.w;
shadowParameters.darkness=shadowMap_normalOffsetScaleDistanceMaxDistanceAndDarkness.w;
vec3 directionEC=positionEC.xyz-shadowMap_lightPositionEC.xyz;
float distance=length(directionEC);
directionEC=normalize(directionEC);
float radius=shadowMap_lightPositionEC.w;
if(distance>radius)
{
return 2.0;
}
vec3 directionWC=czm_inverseViewRotation*directionEC;
shadowParameters.depth=distance/radius-0.0003;
shadowParameters.nDotL=clamp(dot(normalEC,-directionEC),0.,1.);
shadowParameters.texCoords=directionWC;
float visibility=czm_shadowVisibility(shadowMap_textureCube,shadowParameters);
return visibility;
}
bool visible(in vec4 result)
{
result.x/=result.w;
result.y/=result.w;
result.z/=result.w;
return result.x>=-1.&&result.x<=1.
&&result.y>=-1.&&result.y<=1.
&&result.z>=-1.&&result.z<=1.;
}
void main(){
// 釉色 = 结构二维(颜色纹理, 纹理坐标)
gl_FragColor = texture2D(colorTexture, v_textureCoordinates);
// 深度 = 获取深度(结构二维(深度纹理, 纹理坐标))
float depth = getDepth(texture2D(depthTexture, v_textureCoordinates));
// 视角 = (纹理坐标, 深度)
vec4 viewPos = toEye(v_textureCoordinates, depth);
// 世界坐标
vec4 wordPos = czm_inverseView * viewPos;
// 虚拟相机中坐标
vec4 vcPos = camera_view_matrix * wordPos;
float near = .001 * helsing_viewDistance;
float dis = length(vcPos.xyz);
if(dis > near && dis < helsing_viewDistance){
// 透视投影
vec4 posInEye = camera_projection_matrix * vcPos;
// 可视区颜色
// vec4 helsing_visibleAreaColor=vec4(0.,1.,0.,.5);
// vec4 helsing_invisibleAreaColor=vec4(1.,0.,0.,.5);
if(visible(posInEye)){
float vis = shadow(viewPos);
if(vis > 0.3){
gl_FragColor = mix(gl_FragColor,helsing_visibleAreaColor,.5);
} else {
gl_FragColor = mix(gl_FragColor,helsing_invisibleAreaColor,.5);
}
}
}
}`;

View File

@ -0,0 +1,721 @@
// ViewShed.js
import glsl from './glsl'
import Event from "../../../Event";
import MouseTip from "../../../MouseTip";
import Tools from "../../../Tools";
import Controller from "../../../Controller";
import Dialog from '../../../BaseDialog';
import { html } from "./_element";
/**
* @constructor
* @description 可视域分析(测试中)
* @param sdk
* @param {Object} options 选项。
* @param {Cesium.Cartesian3} options.viewPosition 观测点位置。
* @param {Cesium.Cartesian3} options.viewPositionEnd 最远观测点位置(如果设置了观测距离,这个属性可以不设置)。
* @param {Number} options.viewDistance 观测距离(单位`米`)。
* @param {Number} options.viewHeading 航向角(单位`度`)。
* @param {Number} options.viewPitch 俯仰角(单位`度`)。
* @param {Number} options.horizontalViewAngle=90 可视域水平夹角(单位`度`)。
* @param {Number} options.verticalViewAngle=60 可视域垂直夹角(单位`度`)。
* @param {String} options.visibleAreaColor=#008000 可视区域颜色(默认值`绿色`)。
* @param {String} options.invisibleAreaColor=#FF0000 不可视区域颜色(默认值`红色`)。
*/
class ViewShedStage extends Tools {
constructor(sdk, options = {}, _Dialog = {}) {
super(sdk, options)
// if (Object.hasOwn(options.viewPosition, 'lng') && Object.hasOwn(options.viewPosition, 'lat') && Object.hasOwn(options.viewPosition, 'alt')) {
// this.error = '请提供观测点位置!'
// window.ELEMENT && window.ELEMENT.Message({
// message: '请提供观测点位置!',
// type: 'warning',
// duration: 1500
// });
// return
// }
this.viewer = sdk.viewer;
this.options = {}
this.options.viewPosition = options.viewPosition;
this.options.viewPositionEnd = options.viewPositionEnd;
this.options.horizontalViewAngle = (options.horizontalViewAngle || options.horizontalViewAngle === 0) ? options.horizontalViewAngle : 90.0;
this.options.verticalViewAngle = (options.verticalViewAngle || options.verticalViewAngle === 0) ? options.verticalViewAngle : 60.0;
this.options.visibleAreaColor = options.visibleAreaColor || '#008000';
this.options.invisibleAreaColor = options.invisibleAreaColor || '#FF0000';
// this.enabled = (typeof options.enabled === "boolean") ? options.enabled : true;
// this.softShadows = (typeof options.softShadows === "boolean") ? options.softShadows : true;
// this.size = options.size || 10240; // 2048
this.ids = []
this.Dialog = _Dialog
this.html = null
YJ.Analysis.Analyses.push(this)
ViewShedStage.create(this)
// ViewShedStage.edit(this)
// this.update();
}
get viewPosition() {
return this.options.viewPosition
}
set viewPosition(v) {
this.options.viewPosition = v
this.ids[0] && (this.viewer.entities.getById(this.ids[0]).position = new Cesium.Cartesian3.fromDegrees(v.lng, v.lat, v.alt))
this.update()
// let viewPosition3 = Cesium.Cartesian3.fromDegrees(this.options.viewPosition.lng, this.options.viewPosition.lat, this.options.viewPosition.alt)
}
get viewPositionEnd() {
return this.options.viewPositionEnd
}
set viewPositionEnd(v) {
this.options.viewPositionEnd = v
this.ids[1] && (this.viewer.entities.getById(this.ids[1]).position = new Cesium.Cartesian3.fromDegrees(v.lng, v.lat, v.alt))
this.update()
// let viewPositionEnd3 = Cesium.Cartesian3.fromDegrees(this.options.viewPositionEnd.lng, this.options.viewPositionEnd.lat, this.options.viewPositionEnd.alt)
// this.viewDistance = this.viewPositionEnd ? Cesium.Cartesian3.distance(this.viewPosition, this.viewPositionEnd) : (options.viewDistance || 100.0);
}
get horizontalViewAngle() {
return this.options.horizontalViewAngle
}
set horizontalViewAngle(v) {
this.options.horizontalViewAngle = v
if (this._DialogObject && this._DialogObject._element && this._DialogObject._element.content) {
let contentElm = this._DialogObject._element.content
let e_horizontalViewAngle = contentElm.querySelector("input[name='horizontalViewAngle']")
e_horizontalViewAngle.value = v
let rangeNodeActive = contentElm.getElementsByClassName('range-node-active')[0]
let rangeNodeActiveText = rangeNodeActive.getElementsByClassName('range-node-active-text')[0]
rangeNodeActiveText.innerHTML = v + '°';
let rangeProcess = contentElm.getElementsByClassName('range-process')[0]
rangeProcess.style.width = v / 180 * 100 + '%'
}
this.update()
}
get visibleAreaColor() {
return this.options.visibleAreaColor
}
set visibleAreaColor(v) {
this.options.visibleAreaColor = v
this.update()
}
get invisibleAreaColor() {
return this.options.invisibleAreaColor
}
set invisibleAreaColor(v) {
this.options.invisibleAreaColor = v
this.update()
}
get verticalViewAngle() {
return this.options.verticalViewAngle
}
set verticalViewAngle(v) {
this.options.verticalViewAngle = v
this.update()
}
get viewDistance() {
let viewPosition3 = Cesium.Cartesian3.fromDegrees(this.options.viewPosition.lng, this.options.viewPosition.lat, this.options.viewPosition.alt)
let viewPositionEnd3 = Cesium.Cartesian3.fromDegrees(this.options.viewPositionEnd.lng, this.options.viewPositionEnd.lat, this.options.viewPositionEnd.alt)
let distance = Cesium.Cartesian3.distance(viewPosition3, viewPositionEnd3)
return distance
}
get viewHeading() {
let viewPosition3 = Cesium.Cartesian3.fromDegrees(this.options.viewPosition.lng, this.options.viewPosition.lat, this.options.viewPosition.alt)
let viewPositionEnd3 = Cesium.Cartesian3.fromDegrees(this.options.viewPositionEnd.lng, this.options.viewPositionEnd.lat, this.options.viewPositionEnd.alt)
let heading = getHeading(viewPosition3, viewPositionEnd3)
if (this.html) {
let e_viewHeading = this.html.querySelector("span[name='viewHeading']")
e_viewHeading.innerHTML = Number(heading.toFixed(8))
}
return heading
}
get viewPitch() {
let viewPosition3 = Cesium.Cartesian3.fromDegrees(this.options.viewPosition.lng, this.options.viewPosition.lat, this.options.viewPosition.alt)
let viewPositionEnd3 = Cesium.Cartesian3.fromDegrees(this.options.viewPositionEnd.lng, this.options.viewPositionEnd.lat, this.options.viewPositionEnd.alt)
let pitch = getPitch(viewPosition3, viewPositionEnd3)
if (this.html) {
let e_viewPitch = this.html.querySelector("span[name='viewPitch']")
e_viewPitch.innerHTML = Number(pitch.toFixed(8))
}
return pitch
}
static create(that) {
let count = 0;
if (!YJ.Measure.GetMeasureStatus()) {
that.event = new Event(that.sdk)
that.tip = new MouseTip('左键选择观测点位置,右键取消', that.sdk)
YJ.Measure.SetMeasureStatus(true)
that.event.mouse_left((movement, cartesian) => {
if (!that.viewPosition) {
that.options.viewPosition = that.cartesian3Towgs84(cartesian, that.viewer)
that.ids.push(ViewShedStage.create_point(that, cartesian))
that.tip.set_text("左键选择最远观测点位置,右键取消")
}
count++
if (count === 2) {
that.options.viewPositionEnd = that.cartesian3Towgs84(cartesian, that.viewer)
that.ids.push(ViewShedStage.create_point(that, cartesian))
end()
that.update()
}
})
that.event.mouse_move((movement, cartesian) => {
that.tip.setPosition(cartesian, movement.endPosition.x, movement.endPosition.y)
})
that.event.mouse_right((movement, cartesian) => {
that.ids.forEach(id => {
that.viewer.entities.removeById(id)
})
that.ids = []
end()
})
that.event.gesture_pinck_start((movement, cartesian) => {
let startTime = new Date()
that.event.gesture_pinck_end(() => {
let endTime = new Date()
if (endTime - startTime >= 500) {
that.ids.forEach(id => {
that.viewer.entities.removeById(id)
})
that.ids = []
end()
}
})
})
}
else {
console.log('上一次测量未结束')
}
function end() {
that.ids.forEach(id => {
let entity = that.viewer.entities.getById(id)
entity.show = false
})
YJ.Measure.SetMeasureStatus(false)
that.tip.destroy()
that.event.destroy()
that.tip = null
that.event = null
if (count === 2) {
ViewShedStage.edit(that)
}
}
}
static create_point(that, cartesian) {
let id = that.randomString()
let p = that.cartesian3Towgs84(cartesian, that.viewer)
let params = {
id: id,
position: Cesium.Cartesian3.fromDegrees(p.lng, p.lat, p.alt),
billboard: {
image: that.getSourceRootPath() + '/img/point.png',
verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
disableDepthTestDistance: Number.POSITIVE_INFINITY,
color: Cesium.Color.WHITE.withAlpha(0.99)
}
}
that.viewer.entities.add(
new Cesium.Entity(params)
)
return id
}
add() {
this.createLightCamera();
this.createShadowMap();
this.createPostStage();
this.drawFrustumOutline();
this.drawSketch();
}
update() {
this.clear();
this.add();
}
static async edit(that) {
if (that._DialogObject && that._DialogObject.close) {
that._DialogObject.close()
that._DialogObject = null
}
that._DialogObject = await new Dialog(that.sdk.viewer._container, {
title: '可视域分析', left: '180px', top: '100px',
closeCallBack: () => {
that.Dialog.closeCallBack && that.Dialog.closeCallBack()
YJ.Measure.SetMeasureStatus(false)
that.editevent && that.editevent.destroy()
that.ControllerObject && that.ControllerObject.destroy()
that.ids.forEach(id => {
that.viewer.entities.removeById(id)
})
},
})
await that._DialogObject.init()
that._DialogObject._element.body.className = that._DialogObject._element.body.className + ' view-shed'
let contentElm = document.createElement('div');
contentElm.innerHTML = html()
that._DialogObject.contentAppChild(contentElm)
let resetBtn = that._DialogObject._element.body.getElementsByClassName('edit')[0];
resetBtn.addEventListener('click', () => {
that.nodeEdit()
})
that.html = contentElm
//经度值
let e_lng = contentElm.querySelector("span[name='lng']")
e_lng.innerHTML = Number(that.options.viewPosition.lng.toFixed(8))
//纬度值
let e_lat = contentElm.querySelector("span[name='lat']")
e_lat.innerHTML = Number(that.options.viewPosition.lat.toFixed(8))
//高度值
let e_alt = contentElm.querySelector("span[name='alt']")
e_alt.innerHTML = Number(that.options.viewPosition.alt.toFixed(8))
//偏航角
let e_viewHeading = contentElm.querySelector("span[name='viewHeading']")
e_viewHeading.innerHTML = Number(that.viewHeading.toFixed(8))
//俯仰角
let e_viewPitch = contentElm.querySelector("span[name='viewPitch']")
e_viewPitch.innerHTML = Number(that.viewPitch.toFixed(8))
//视域夹角
let e_horizontalViewAngle = contentElm.querySelector("input[name='horizontalViewAngle']")
e_horizontalViewAngle.value = that.options.horizontalViewAngle
let rangeNodeActive = contentElm.getElementsByClassName('range-node-active')[0]
let rangeNodeActiveText = rangeNodeActive.getElementsByClassName('range-node-active-text')[0]
let rangeProcess = contentElm.getElementsByClassName('range-process')[0]
let percentage = that.horizontalViewAngle / 180 * 100
rangeNodeActive.style.left = percentage + '%';
rangeProcess.style.width = percentage + '%'
rangeNodeActiveText.innerHTML = that.horizontalViewAngle + '°';
let timeout
e_horizontalViewAngle.addEventListener('input', () => {
let percentage = e_horizontalViewAngle.value / 180 * 100
rangeNodeActive.style.left = percentage + '%';
rangeProcess.style.width = percentage + '%';
rangeNodeActiveText.innerHTML = e_horizontalViewAngle.value + '°';
})
e_horizontalViewAngle.addEventListener('change', () => {
clearTimeout(timeout)
timeout = setTimeout(() => {
that.horizontalViewAngle = e_horizontalViewAngle.value;
}, 300);
});
}
clear() {
YJ.Measure.SetMeasureStatus(false)
this.tip && this.tip.destroy()
this.event && this.event.destroy()
this.tip = null
this.event = null
if (this.sketch) {
this.viewer.entities.removeById(this.sketch.id);
this.sketch = null;
}
if (this.frustumOutline) {
this.frustumOutline.destroy();
this.frustumOutline = null;
}
if (this.FrustumBottomSurface) {
this.FrustumBottomSurface.destroy();
this.FrustumBottomSurface = null;
}
if (this.postStage) {
this.viewer.scene.postProcessStages.remove(this.postStage);
this.postStage = null;
}
}
destroy() {
this.clear()
this.editevent && this.editevent.destroy()
this.ControllerObject && this.ControllerObject.destroy()
this.ids.forEach(id => {
this.viewer.entities.removeById(id)
})
YJ.Measure.SetMeasureStatus(false)
}
nodeEdit() {
if (YJ.Measure.GetMeasureStatus()) {
console.log('上一次测量未结束')
}
else {
this.editevent && this.editevent.destroy()
this.ids.forEach(id => {
let entity = this.viewer.entities.getById(id)
entity.show = true
})
let selectPoint
YJ.Measure.SetMeasureStatus(true)
// this.tip = new MouseTip('左键选择要操作的观测点,右键取消', this.sdk)
this.editevent = new Event(this.sdk)
this.editevent.mouse_left((movement, cartesian) => {
let pick = this.viewer.scene.pick(movement.position);
if (pick && pick.id && pick.id.id && this.ids.indexOf(pick.id.id) != -1 && (!selectPoint || selectPoint.id != pick.id.id)) {
selectPoint = pick.id
// this.event.destroy()
// this.tip.destroy()
this.viewer.entities.getById(this.ids[0]).position = new Cesium.Cartesian3.fromDegrees(this.viewPosition.lng, this.viewPosition.lat, this.viewPosition.alt)
this.viewer.entities.getById(this.ids[1]).position = new Cesium.Cartesian3.fromDegrees(this.viewPositionEnd.lng, this.viewPositionEnd.lat, this.viewPositionEnd.alt)
this.viewPosition
this.ControllerObject && this.ControllerObject.destroy()
console.log(this.cartesian3Towgs84(selectPoint.position._value, this.sdk.viewer))
this.ControllerObject = new Controller(this.sdk, { position: { ...this.cartesian3Towgs84(selectPoint.position._value, this.sdk.viewer) } })
this.ControllerObject.controllerCallBack = (params, status) => {
if (params.position.alt < 0) {
params.position.alt = 0
}
selectPoint.position = new Cesium.Cartesian3.fromDegrees(params.position.lng, params.position.lat, params.position.alt)
if (status) {
if (this.ids.indexOf(pick.id.id) == 0) {
this.viewPosition = params.position
}
else {
this.viewPositionEnd = params.position
}
YJ.Measure.SetMeasureStatus(true)
}
}
this.ControllerObject.editTranslational()
}
})
this.editevent.mouse_right((movement, cartesian) => {
YJ.Measure.SetMeasureStatus(false)
this.editevent && this.editevent.destroy()
this.ControllerObject && this.ControllerObject.destroy()
this.ids.forEach(id => {
let entity = this.viewer.entities.getById(id)
entity.show = false
})
selectPoint = null
})
this.editevent.mouse_move((movement, cartesian) => {
// this.tip.setPosition(
// cartesian,
// movement.endPosition.x,
// movement.endPosition.y
// )
})
this.editevent.gesture_pinck_start((movement, cartesian) => {
let startTime = new Date()
this.editevent.gesture_pinck_end(() => {
let endTime = new Date()
if (endTime - startTime >= 500) {
YJ.Measure.SetMeasureStatus(false)
this.editevent && this.editevent.destroy()
this.ControllerObject && this.ControllerObject.destroy()
this.ids.forEach(id => {
let entity = this.viewer.entities.getById(id)
entity.show = false
})
selectPoint = null
}
})
})
}
}
createLightCamera() {
let _this = this
this.lightCamera = new Cesium.Camera(this.viewer.scene);
this.lightCamera.position = Cesium.Cartesian3.fromDegrees(this.options.viewPosition.lng, this.options.viewPosition.lat, this.options.viewPosition.alt);
// if (this.viewPositionEnd) {
// let direction = Cesium.Cartesian3.normalize(Cesium.Cartesian3.subtract(this.viewPositionEnd, this.viewPosition, new Cesium.Cartesian3()), new Cesium.Cartesian3());
// this.lightCamera.direction = direction; // direction是相机面向的方向
// }
this.lightCamera.frustum.near = this.viewDistance * 0.001;
this.lightCamera.frustum.far = this.viewDistance;
const hr = Cesium.Math.toRadians(this.horizontalViewAngle);
const vr = Cesium.Math.toRadians(this.verticalViewAngle);
const aspectRatio =
(this.viewDistance * Math.tan(hr / 2) * 2) /
(this.viewDistance * Math.tan(vr / 2) * 2);
this.lightCamera.frustum.aspectRatio = aspectRatio;
if (hr > vr) {
this.lightCamera.frustum.fov = hr;
} else {
this.lightCamera.frustum.fov = vr;
}
this.lightCamera.setView({
destination: Cesium.Cartesian3.fromDegrees(this.options.viewPosition.lng, this.options.viewPosition.lat, this.options.viewPosition.alt),
orientation: {
heading: Cesium.Math.toRadians(this.viewHeading || 0),
pitch: Cesium.Math.toRadians(this.viewPitch || 0),
roll: 0
}
});
}
createShadowMap() {
this.shadowMap = new Cesium.ShadowMap({
context: (this.viewer.scene).context,
lightCamera: this.lightCamera,
enabled: true,
isPointLight: true,
pointLightRadius: this.viewDistance,
cascadesEnabled: false,
size: 2048, // 2048
softShadows: true,
normalOffset: false,
fromLightSource: false
});
this.viewer.scene.shadowMap = this.shadowMap;
}
createPostStage() {
const fs = glsl
const postStage = new Cesium.PostProcessStage({
fragmentShader: fs,
uniforms: {
shadowMap_textureCube: () => {
this.shadowMap.update(Reflect.get(this.viewer.scene, "_frameState"));
return Reflect.get(this.shadowMap, "_shadowMapTexture");
},
shadowMap_matrix: () => {
this.shadowMap.update(Reflect.get(this.viewer.scene, "_frameState"));
return Reflect.get(this.shadowMap, "_shadowMapMatrix");
},
shadowMap_lightPositionEC: () => {
this.shadowMap.update(Reflect.get(this.viewer.scene, "_frameState"));
return Reflect.get(this.shadowMap, "_lightPositionEC");
},
shadowMap_normalOffsetScaleDistanceMaxDistanceAndDarkness: () => {
this.shadowMap.update(Reflect.get(this.viewer.scene, "_frameState"));
const bias = this.shadowMap._pointBias;
return Cesium.Cartesian4.fromElements(
bias.normalOffsetScale,
this.shadowMap._distance,
this.shadowMap.maximumDistance,
0.0,
new Cesium.Cartesian4()
);
},
shadowMap_texelSizeDepthBiasAndNormalShadingSmooth: () => {
this.shadowMap.update(Reflect.get(this.viewer.scene, "_frameState"));
const bias = this.shadowMap._pointBias;
const scratchTexelStepSize = new Cesium.Cartesian2();
const texelStepSize = scratchTexelStepSize;
texelStepSize.x = 1.0 / this.shadowMap._textureSize.x;
texelStepSize.y = 1.0 / this.shadowMap._textureSize.y;
return Cesium.Cartesian4.fromElements(
texelStepSize.x,
texelStepSize.y,
bias.depthBias,
bias.normalShadingSmooth,
new Cesium.Cartesian4()
);
},
camera_projection_matrix: this.lightCamera.frustum.projectionMatrix,
camera_view_matrix: this.lightCamera.viewMatrix,
helsing_viewDistance: () => {
return this.viewDistance;
},
helsing_visibleAreaColor: Cesium.Color.fromCssColorString(this.visibleAreaColor),
helsing_invisibleAreaColor: Cesium.Color.fromCssColorString(this.invisibleAreaColor),
shadowMap: this.shadowMap,
far: () => {
return this.viewDistance;
},
}
});
this.postStage = this.viewer.scene.postProcessStages.add(postStage);
}
drawFrustumOutline() {
const scratchRight = new Cesium.Cartesian3();
const scratchRotation = new Cesium.Matrix3();
const scratchOrientation = new Cesium.Quaternion();
const position = this.lightCamera.positionWC;
const direction = this.lightCamera.directionWC;
const up = this.lightCamera.upWC;
let right = this.lightCamera.rightWC;
right = Cesium.Cartesian3.negate(right, scratchRight);
let rotation = scratchRotation;
Cesium.Matrix3.setColumn(rotation, 0, right, rotation);
Cesium.Matrix3.setColumn(rotation, 1, up, rotation);
Cesium.Matrix3.setColumn(rotation, 2, direction, rotation);
let orientation = Cesium.Quaternion.fromRotationMatrix(rotation, scratchOrientation);
let instance = new Cesium.GeometryInstance({
geometry: new Cesium.FrustumOutlineGeometry({
frustum: this.lightCamera.frustum,
origin: Cesium.Cartesian3.fromDegrees(this.options.viewPosition.lng, this.options.viewPosition.lat, this.options.viewPosition.alt),
orientation: orientation
}),
id: Math.random().toString(36).substr(2),
attributes: {
color: Cesium.ColorGeometryInstanceAttribute.fromColor(
Cesium.Color.YELLOWGREEN//new Cesium.Color(0.0, 1.0, 0.0, 1.0)
),
show: new Cesium.ShowGeometryInstanceAttribute(true)
}
});
let frustum = this.lightCamera.frustum.clone()
frustum.near = frustum.far - 100
let instance2 = new Cesium.GeometryInstance({
geometry: new Cesium.FrustumGeometry({
frustum: frustum,
origin: Cesium.Cartesian3.fromDegrees(this.options.viewPosition.lng, this.options.viewPosition.lat, this.options.viewPosition.alt),
orientation: orientation,
vertexFormat: Cesium.VertexFormat.POSITION_ONLY,
}),
id: Math.random().toString(36).substr(2),
attributes: {
color: Cesium.ColorGeometryInstanceAttribute.fromColor(
Cesium.Color.YELLOWGREEN//new Cesium.Color(0.0, 1.0, 0.0, 1.0)
),
show: new Cesium.ShowGeometryInstanceAttribute(true)
}
});
this.frustumOutline = this.viewer.scene.primitives.add(
new Cesium.Primitive({
geometryInstances: [instance],
appearance: new Cesium.PerInstanceColorAppearance({
flat: true,
translucent: false
})
})
);
const radius = this.viewDistance;
const angle = this.viewHeading;
let Ea = 6378137; // 赤道半径
let Eb = 6356725; // 极半径
const lng = this.options.viewPosition.lng,
lat = this.options.viewPosition.lat;
let positionArr = [];
let boundaryPoints = [];
positionArr.push(lng, lat);
boundaryPoints.push([lng, lat]);
//正北是0°
let start = angle + 45 > 360 ? angle - 45 - 360 : angle - 45;
let end = start + 90;
for (let i = start; i <= end; i++) {
let dx = radius * Math.sin((i * Math.PI) / 180.0);
let dy = radius * Math.cos((i * Math.PI) / 180.0);
let ec = Eb + ((Ea - Eb) * (90.0 - lat)) / 90.0;
let ed = ec * Math.cos((lat * Math.PI) / 180);
let BJD = lng + ((dx / ed) * 180.0) / Math.PI;
let BWD = lat + ((dy / ec) * 180.0) / Math.PI;
positionArr.push(BJD, BWD);
}
let polygon = new Cesium.PolygonGeometry({
polygonHierarchy: new Cesium.PolygonHierarchy(
Cesium.Cartesian3.fromDegreesArray(positionArr)
),
height: 0.0,
extrudedHeight: 0.0,
vertexFormat: Cesium.PerInstanceColorAppearance.VERTEX_FORMAT,
stRotation: 0.0, // 纹理的旋转坐标(以弧度为单位),正旋转是逆时针方向
ellipsoid: Cesium.Ellipsoid.WGS84,
granularity: Cesium.Math.RADIANS_PER_DEGREE, // 每个纬度和经度之间的距离(以弧度为单位),确定缓冲区中的位置数
perPositionHeight: false, // 每个位置点使用的高度
closeTop: true,
closeBottom: true,
// NONE 与椭圆表面不符的直线;GEODESIC 遵循测地路径;RHUMB 遵循大黄蜂或恶魔般的道路。
arcType: Cesium.ArcType.GEODESIC, // 多边形边缘线型
});
let polygonInstance = new Cesium.GeometryInstance({
geometry: polygon,
name: "ViewershedPolygon",
attributes: {
color: Cesium.ColorGeometryInstanceAttribute.fromColor(
Cesium.Color.BLUE
),
show: new Cesium.ShowGeometryInstanceAttribute(true), //显示或者隐藏
},
});
// this.FrustumBottomSurface = this.viewer.scene.primitives.add(
// new Cesium.GroundPrimitive({
// geometryInstances: polygonInstance,
// appearance: new Cesium.PerInstanceColorAppearance({
// translucent: true, //false时透明度无效
// closed: false,
// }),
// })
// );
}
drawSketch() {
this.sketch = this.viewer.entities.add({
name: 'sketch',
position: Cesium.Cartesian3.fromDegrees(this.options.viewPosition.lng, this.options.viewPosition.lat, this.options.viewPosition.alt),
orientation: Cesium.Transforms.headingPitchRollQuaternion(
Cesium.Cartesian3.fromDegrees(this.options.viewPosition.lng, this.options.viewPosition.lat, this.options.viewPosition.alt),
Cesium.HeadingPitchRoll.fromDegrees(this.viewHeading - 90, this.viewPitch, 0.0)
),
ellipsoid: {
radii: new Cesium.Cartesian3(
this.viewDistance,
this.viewDistance,
this.viewDistance
),
// innerRadii: new Cesium.Cartesian3(2.0, 2.0, 2.0),
minimumClock: Cesium.Math.toRadians(-this.horizontalViewAngle / 2),
maximumClock: Cesium.Math.toRadians(this.horizontalViewAngle / 2),
minimumCone: Cesium.Math.toRadians(this.verticalViewAngle + 7.75),
maximumCone: Cesium.Math.toRadians(180 - this.verticalViewAngle - 7.75),
fill: false,
outline: true,
subdivisions: 256,
stackPartitions: 64,
slicePartitions: 64,
outlineColor: Cesium.Color.YELLOWGREEN
}
});
}
}
function getHeading(fromPosition, toPosition) {
let finalPosition = new Cesium.Cartesian3();
let matrix4 = Cesium.Transforms.eastNorthUpToFixedFrame(fromPosition);
Cesium.Matrix4.inverse(matrix4, matrix4);
Cesium.Matrix4.multiplyByPoint(matrix4, toPosition, finalPosition);
Cesium.Cartesian3.normalize(finalPosition, finalPosition);
return Cesium.Math.toDegrees(Math.atan2(finalPosition.x, finalPosition.y));
}
function getPitch(fromPosition, toPosition) {
let finalPosition = new Cesium.Cartesian3();
let matrix4 = Cesium.Transforms.eastNorthUpToFixedFrame(fromPosition);
Cesium.Matrix4.inverse(matrix4, matrix4);
Cesium.Matrix4.multiplyByPoint(matrix4, toPosition, finalPosition);
Cesium.Cartesian3.normalize(finalPosition, finalPosition);
return Cesium.Math.toDegrees(Math.asin(finalPosition.z));
}
export default ViewShedStage;

View File

@ -0,0 +1,74 @@
function html() {
return `
<span class="custom-divider"></span>
<div class="div-item">
<div class="row">
<div class="col">
<span class="label">位置拾取(起点、终点)</span>
<button class="edit"><svg class="icon-edit"><use xlink:href="#yj-icon-edit"></use></svg>拾取</button>
</div>
</div>
</div>
<span class="custom-divider"></span>
<div class="div-item">
<div class="row subtitle-box">
<span class="subtitle">视域夹角</span>
</div>
<div class="row">
<div class="col">
<div class="range-box">
<div class="range-bg">
<div class="range-process-box">
<div class="range-process"></div>
</div>
</div>
<div class="range-node-box">
<span class="range-node-text">0°</span>
<span class="range-node-text">45°</span>
<span class="range-node-text">90°</span>
<span class="range-node-text">135°</span>
<span class="range-node-text">180°</span>
<div class="range-node-active"><span class="range-node-active-text">0°</span></div>
</div>
<input type="range" max="180" min="0" step="1" name="horizontalViewAngle">
</div>
</div>
</div>
</div>
<span class="custom-divider"></span>
<div class="div-item">
<div class="row">
<div class="col">
<span class="label">经度:</span>
<span class="text-number" name="lng"></span>
</div>
<div class="col">
<span class="label">偏航角:</span>
<span class="text-number" name="viewHeading"></span>
<span class="unit">°</span>
</div>
</div>
<div class="row">
<div class="col">
<span class="label">纬度:</span>
<span class="text-number" name="lat"></span>
</div>
<div class="col">
<span class="label">俯仰角:</span>
<span class="text-number" name="viewPitch"></span>
<span class="unit">°</span>
</div>
</div>
<div class="row">
<div class="col">
<span class="label">高度:</span>
<span class="text-number" name="alt"></span>
<span class="unit text-number" style="margin-left: 5px;">m</span>
</div>
</div>
</div>
<span class="custom-divider"></span>
`
}
export { html }

View File

@ -0,0 +1,512 @@
/*
* @Author: Wang jianLei
* @Date: 2022-05-17 21:49:28
* @Last Modified by: Wang JianLei
* @Last Modified time: 2022-05-19 22:08:14
*/
let ViewShed = function (sdk, canvasEleId) {
if (!sdk.viewer) throw new Error("no viewer object!");
alert(canvasEleId)
let canvasEle = document.getElementById(canvasEleId);
if (!canvasEle) throw new Error("the canvas element is not exist");
this.canvasEle = canvasEle;
this.viewer = sdk.viewer;
this.handler = undefined;
this.lightCamera;
this.pyramid;
this.frustumPrimitive;
this.viewershedPolygon;
};
ViewShed.prototype = {
/**
* 初始化handler
*/
initHandler() {
if (this.handler) {
this.handler.destroy();
this.handler = undefined;
}
},
/**
* 开始执行视域分析
* @param {number} precision 精度值越大创建耗时越长建议在10~20之间
*/
createViewshed: function (precision) {
let $this = this;
let scene = $this.viewer.scene;
$this.initHandler();
$this.clearAll();
$this.handler = new Cesium.ScreenSpaceEventHandler($this.viewer.canvas);
$this.handler.setInputAction((event) => {
// 禁止地球旋转和缩放,地球的旋转会对鼠标移动监听有影响,所以需要禁止
scene.screenSpaceCameraController.enableRotate = false;
scene.screenSpaceCameraController.enableZoom = false;
scene.globe.depthTestAgainstTerrain = true;
let earthPosition = scene.pickPosition(event.position);
let pos = $this.cartesian3ToDegree(earthPosition);
$this.handler.setInputAction(function (event) {
let newPosition = scene.pickPosition(event.endPosition);
if (Cesium.defined(newPosition)) {
let pos1 = $this.cartesian3ToDegree(newPosition);
let distance = Cesium.Cartesian3.distance(newPosition, earthPosition);
let angle = $this.getAngle(pos[0], pos[1], pos1[0], pos1[1]);
let pitch = $this.getPitch(earthPosition, newPosition);
$this.ViewShedOptions = {
viewPosition: earthPosition, //观测点 笛卡尔坐标
endPosition: newPosition, //目标点 笛卡尔坐标
direction: angle, //观测方位角 默认为`0`,范围`0~360`
pitch: pitch, //俯仰角,radius,默认为`0`
horizontalViewAngle: 90, //可视域水平夹角,默认为 `90`,范围`0~360`
verticalViewAngle: 60, //可视域垂直夹角,默认为`60`,范围`0~180`
visibleAreaColor: Cesium.Color.GREEN, //可视区域颜色,默认为`green`
invisibleAreaColor: Cesium.Color.RED, //不可见区域颜色,默认为`red`
visualRange: distance, //距离,单位`米`
};
$this.updateViewShed();
}
}, Cesium.ScreenSpaceEventType.MOUSE_MOVE);
}, Cesium.ScreenSpaceEventType.LEFT_DOWN);
$this.handler.setInputAction(() => {
$this.initHandler();
// 开启地球旋转和缩放
scene.screenSpaceCameraController.enableRotate = true;
scene.screenSpaceCameraController.enableZoom = true;
$this.drawViewershed(precision);
}, Cesium.ScreenSpaceEventType.LEFT_UP);
},
ReturnDistance(pos0, pos1) {
let distance = 0;
let point1cartographic = Cesium.Cartographic.fromCartesian(pos0);
let point2cartographic = Cesium.Cartographic.fromCartesian(pos1);
/**根据经纬度计算出距离**/
let geodesic = new Cesium.EllipsoidGeodesic();
geodesic.setEndPoints(point1cartographic, point2cartographic);
let s = geodesic.surfaceDistance;
return s;
},
getHeight(x, y, objectsToExclude) {
let endCartographic = Cesium.Cartographic.fromDegrees(x, y);
let endHeight = this.viewer.scene.sampleHeight(
endCartographic,
objectsToExclude
);
return endHeight;
},
cartesian3ToDegree: function (Cartesian3) {
let _ellipsoid = this.viewer.scene.globe.ellipsoid;
let _cartographic = _ellipsoid.cartesianToCartographic(Cartesian3);
let _lat = Cesium.Math.toDegrees(_cartographic.latitude);
let _lng = Cesium.Math.toDegrees(_cartographic.longitude);
let _alt = _cartographic.height;
return [_lng, _lat, _alt];
},
getAngle: function (lng1, lat1, lng2, lat2) {
let dRotateAngle = Math.atan2(Math.abs(lng1 - lng2), Math.abs(lat1 - lat2));
if (lng2 >= lng1) {
dRotateAngle = lat2 < lat1 ? Math.PI - dRotateAngle : dRotateAngle;
} else {
dRotateAngle =
lat2 >= lat1 ? 2 * Math.PI - dRotateAngle : Math.PI + dRotateAngle;
}
dRotateAngle = (dRotateAngle * 180) / Math.PI;
return dRotateAngle;
},
getPitch(pointA, pointB) {
let transfrom = Cesium.Transforms.eastNorthUpToFixedFrame(pointA);
const vector = Cesium.Cartesian3.subtract(
pointB,
pointA,
new Cesium.Cartesian3()
);
let direction = Cesium.Matrix4.multiplyByPointAsVector(
Cesium.Matrix4.inverse(transfrom, transfrom),
vector,
vector
);
Cesium.Cartesian3.normalize(direction, direction);
return Cesium.Math.PI_OVER_TWO - Cesium.Math.acosClamped(direction.z);
},
updateViewShed: function () {
this.clear();
this.setLightCamera();
this.addVisualPyramid();
this.createFrustum();
},
clear: function () {
if (this.pyramid) {
this.viewer.entities.removeById(this.pyramid.id);
this.pyramid = undefined;
}
if (this.frustumPrimitive) {
this.viewer.scene.primitives.remove(this.frustumPrimitive);
this.frustumPrimitive = undefined;
}
if (this.debugModelMatrixPrimitive) {
this.viewer.scene.primitives.remove(this.debugModelMatrixPrimitive);
this.debugModelMatrixPrimitive = undefined;
}
},
clearAll: function () {
this.clear();
if (this.viewershedPolygon) {
this.viewer.scene.primitives.remove(this.viewershedPolygon);
this.viewershedPolygon = undefined;
}
},
addVisualPyramid: function () {
let options = this.ViewShedOptions;
let position = options.viewPosition;
let visualRange = Number(options.visualRange);
let transform = Cesium.Transforms.eastNorthUpToFixedFrame(position);
this.debugModelMatrixPrimitive = this.viewer.scene.primitives.add(
new Cesium.DebugModelMatrixPrimitive({
modelMatrix: transform,
length: 5.0,
})
);
const halfClock = options.horizontalViewAngle / 2;
const halfCone = options.verticalViewAngle / 2;
const pitch = Cesium.Math.toDegrees(options.pitch);
const ellipsoid = new Cesium.EllipsoidGraphics({
radii: new Cesium.Cartesian3(visualRange, visualRange, visualRange),
minimumClock: Cesium.Math.toRadians(90 - options.direction - halfClock),
maximumClock: Cesium.Math.toRadians(90 - options.direction + halfClock),
minimumCone: Cesium.Math.toRadians(90 - pitch - halfCone),
maximumCone: Cesium.Math.toRadians(90 - pitch + halfCone),
fill: false,
outline: true,
subdivisions: 256,
stackPartitions: 64,
slicePartitions: 64,
outlineColor: Cesium.Color.YELLOWGREEN.withAlpha(0.5),
});
const pyramidEntity = new Cesium.Entity({
position: position,
ellipsoid,
});
this.pyramid = this.viewer.entities.add(pyramidEntity);
},
setLightCamera: function () {
if (!this.lightCamera) {
this.lightCamera = new Cesium.Camera(this.viewer.scene);
}
let options = this.ViewShedOptions;
let visualRange = Number(options.visualRange);
this.lightCamera.position = options.viewPosition;
this.lightCamera.frustum.near = 0.1;
this.lightCamera.frustum.far = visualRange;
const hr = Cesium.Math.toRadians(options.horizontalViewAngle);
const vr = Cesium.Math.toRadians(options.verticalViewAngle);
this.lightCamera.frustum.aspectRatio =
(visualRange * Math.tan(hr / 2) * 2) /
(visualRange * Math.tan(vr / 2) * 2);
this.lightCamera.frustum.fov = hr > vr ? hr : vr;
this.lightCamera.setView({
destination: options.viewPosition,
orientation: {
heading: Cesium.Math.toRadians(options.direction || 0),
pitch: options.pitch || 0,
roll: 0,
},
});
},
createFrustum: function () {
const scratchRight = new Cesium.Cartesian3();
const scratchRotation = new Cesium.Matrix3();
const scratchOrientation = new Cesium.Quaternion();
const direction = this.lightCamera.directionWC;
const up = this.lightCamera.upWC;
let right = this.lightCamera.rightWC;
right = Cesium.Cartesian3.negate(right, scratchRight);
let rotation = scratchRotation;
Cesium.Matrix3.setColumn(rotation, 0, right, rotation);
Cesium.Matrix3.setColumn(rotation, 1, up, rotation);
Cesium.Matrix3.setColumn(rotation, 2, direction, rotation);
let orientation = Cesium.Quaternion.fromRotationMatrix(
rotation,
scratchOrientation
);
let instanceOutline = new Cesium.GeometryInstance({
geometry: new Cesium.FrustumOutlineGeometry({
frustum: this.lightCamera.frustum,
origin: this.ViewShedOptions.viewPosition,
orientation: orientation,
}),
id: "视椎体轮廓线" + Math.random().toString(36).substr(2),
attributes: {
color: Cesium.ColorGeometryInstanceAttribute.fromColor(
new Cesium.Color(0.0, 1.0, 0.0, 1.0)
),
show: new Cesium.ShowGeometryInstanceAttribute(true),
},
});
this.frustumPrimitive = this.viewer.scene.primitives.add(
new Cesium.Primitive({
geometryInstances: instanceOutline,
appearance: new Cesium.PerInstanceColorAppearance({
flat: true,
translucent: false,
closed: true,
}),
})
);
},
createPoint: function (firstPos, secondPos) {
let entity4FirstPos = new Cesium.Entity({
name: "firstPos",
show: true,
position: firstPos,
point: {
show: true,
pixelSize: 20,
color: Cesium.Color.RED,
outlineColor: Cesium.Color.YELLOW,
outlineWidth: 5,
},
description: `
<p>这是绘制的视椎体起点</p>`,
});
this.viewer.entities.add(entity4FirstPos);
let entity4SecondPos = new Cesium.Entity({
name: "secondPos",
show: true,
position: secondPos,
point: {
show: true,
pixelSize: 30,
color: Cesium.Color.YELLOW,
outlineColor: Cesium.Color.RED,
outlineWidth: 8,
},
description: `
<p>这是绘制的视椎体视角终点</p>`,
});
this.viewer.entities.add(entity4SecondPos);
},
//绘制可视域
add(positionArr) {
let polygon = new Cesium.PolygonGeometry({
polygonHierarchy: new Cesium.PolygonHierarchy(
Cesium.Cartesian3.fromDegreesArray(positionArr)
),
height: 0.0,
extrudedHeight: 0.0,
vertexFormat: Cesium.PerInstanceColorAppearance.VERTEX_FORMAT,
stRotation: 0.0, // 纹理的旋转坐标(以弧度为单位),正旋转是逆时针方向
ellipsoid: Cesium.Ellipsoid.WGS84,
granularity: Cesium.Math.RADIANS_PER_DEGREE, // 每个纬度和经度之间的距离(以弧度为单位),确定缓冲区中的位置数
perPositionHeight: false, // 每个位置点使用的高度
closeTop: true,
closeBottom: true,
// NONE 与椭圆表面不符的直线;GEODESIC 遵循测地路径;RHUMB 遵循大黄蜂或恶魔般的道路。
arcType: Cesium.ArcType.GEODESIC, // 多边形边缘线型
});
let polygonInstance = new Cesium.GeometryInstance({
geometry: polygon,
name: "ViewershedPolygon",
attributes: {
color: Cesium.ColorGeometryInstanceAttribute.fromColor(
Cesium.Color.BLUE.withAlpha(0.6)
),
show: new Cesium.ShowGeometryInstanceAttribute(true), //显示或者隐藏
},
});
this.viewershedPolygon = this.viewer.scene.primitives.add(
new Cesium.GroundPrimitive({
geometryInstances: polygonInstance,
appearance: new Cesium.EllipsoidSurfaceAppearance({
aboveGround: true,
material: new Cesium.Material({
fabric: {
type: "Image",
uniforms: {
image: this.returnImgae(),
},
},
}),
}),
})
);
},
drawViewershed(precision) {
const pos = this.cartesian3ToDegree(this.ViewShedOptions.viewPosition);
const radius = this.ViewShedOptions.visualRange;
const direction = this.ViewShedOptions.direction;
let boundary = this.computeBoundaryOptions(pos, radius, direction);
const bbox = boundary.bbox;
let mask = turf.polygon([boundary.boundaryPoints]);
const dis = this.ViewShedOptions.visualRange / (precision * 1000);
let gridPoints = turf.pointGrid(bbox, dis, { mask: mask });
let pointsResult = this.createTargetPoints(gridPoints, dis, pos);
let variogram = kriging.train(
pointsResult.values,
pointsResult.lngs,
pointsResult.lats,
"exponential",
0,
100
);
let grid = kriging.grid([boundary.boundaryPoints], variogram, dis / 1000);
const colors = [
"#ff000080",
"#ff000080",
"#ff000080",
"#ff000080",
"#ff000080",
"#ff000080",
"#00ff0080",
"#00ff0080",
"#00ff0080",
"#00ff0080",
"#00ff0080",
"#00ff0080",
];
this.canvasEle.width = 3840;
this.canvasEle.height = 2160;
kriging.plot(
this.canvasEle,
grid,
[bbox[0], bbox[2]],
[bbox[1], bbox[3]],
colors
);
this.add(boundary.positionArr);
},
computeBoundaryOptions(pos, radius, angle) {
let Ea = 6378137; // 赤道半径
let Eb = 6356725; // 极半径
const lng = pos[0],
lat = pos[1];
const bbox = [lng, lat, lng, lat]; //[minX, minY, maxX, maxY]
let positionArr = [];
let boundaryPoints = [];
positionArr.push(lng, lat);
boundaryPoints.push([lng, lat]);
//正北是0°
let start = angle + 45 > 360 ? angle - 45 - 360 : angle - 45;
let end = start + 90;
for (let i = start; i <= end; i++) {
let dx = radius * Math.sin((i * Math.PI) / 180.0);
let dy = radius * Math.cos((i * Math.PI) / 180.0);
let ec = Eb + ((Ea - Eb) * (90.0 - lat)) / 90.0;
let ed = ec * Math.cos((lat * Math.PI) / 180);
let BJD = lng + ((dx / ed) * 180.0) / Math.PI;
let BWD = lat + ((dy / ec) * 180.0) / Math.PI;
positionArr.push(BJD, BWD);
boundaryPoints.push([BJD, BWD]);
this.refreshBBox(bbox, BJD, BWD);
}
boundaryPoints.push([lng, lat]);
return {
positionArr,
boundaryPoints,
bbox,
};
},
/**
* 更新外围矩形 Bbox
* @param {Array} result 外围矩形Bbox-[minX, minY, maxX, maxY]
* @param {Number} x 经度
* @param {Number} y 纬度
*/
refreshBBox(result, x, y) {
result[0] = x < result[0] ? x : result[0];
result[1] = y < result[1] ? y : result[1];
result[2] = x > result[2] ? x : result[2];
result[3] = y > result[3] ? y : result[3];
},
/**
* 插值点用射线判断通视性
* @param {*} gridPoints 网格点
* @param {*} step 步长,可以理解成是精度
* @param {*} sourcePos 视域分析起点
* @returns kriging插值所需的参数对象{ values:[], lngs:[], lats:[]}
*/
createTargetPoints(gridPoints, step, sourcePos) {
let positionArr = [];
let objectsToExclude = [
this.frustumPrimitive,
this.pyramid,
this.debugModelMatrixPrimitive,
];
let values = [],
lngs = [],
lats = [];
let height = this.getHeight(sourcePos[0], sourcePos[1], objectsToExclude);
positionArr.push({
x: sourcePos[0],
y: sourcePos[1],
z: height,
});
let viewPoint = this.ViewShedOptions.viewPosition;
for (let index = 0; index < gridPoints.features.length; index++) {
const feature = gridPoints.features[index];
const coords = feature.geometry.coordinates;
const x = coords[0],
y = coords[1];
let h = this.getHeight(x, y, objectsToExclude);
let endPoint = Cesium.Cartesian3.fromDegrees(x, y, h);
let direction = Cesium.Cartesian3.normalize(
Cesium.Cartesian3.subtract(
endPoint,
viewPoint,
new Cesium.Cartesian3()
),
new Cesium.Cartesian3()
);
// 建立射线
let ray = new Cesium.Ray(viewPoint, direction);
let result = this.viewer.scene.pickFromRay(ray, objectsToExclude); // 计算交互点,返回第一个
if (result) {
let buffer = this.ReturnDistance(endPoint, result.position);
// let M_color = Cesium.Color.GREEN;
if (buffer > step) {
// M_color = Cesium.Color.RED;
values.push(0);
} else {
values.push(1);
}
lngs.push(x);
lats.push(y);
// this.viewer.entities.add(
// new Cesium.Entity({
// name: "插值点哦",
// show: true,
// position: endPoint,
// point: {
// show: true,
// pixelSize: 10,
// color: M_color,
// outlineWidth: 2,
// outlineColor: Cesium.Color.YELLOW,
// },
// })
// );
}
}
return {
values,
lngs,
lats,
};
},
/**
* canvas转image图片
* @returns base64图片
*/
returnImgae() {
return this.canvasEle.toDataURL("image/png");
},
};
export default ViewShed;

View File

@ -0,0 +1,131 @@
export default `
#define USE_CUBE_MAP_SHADOW true
uniform sampler2D colorTexture;
uniform sampler2D depthTexture;
varying vec2 v_textureCoordinates;
uniform mat4 camera_projection_matrix;
uniform mat4 camera_view_matrix;
uniform samplerCube shadowMap_textureCube;
uniform mat4 shadowMap_matrix;
uniform vec4 shadowMap_lightPositionEC;
uniform vec4 shadowMap_normalOffsetScaleDistanceMaxDistanceAndDarkness;
uniform vec4 shadowMap_texelSizeDepthBiasAndNormalShadingSmooth;
uniform float helsing_viewDistance;
uniform vec4 helsing_visibleAreaColor;
uniform vec4 helsing_invisibleAreaColor;
struct zx_shadowParameters
{
vec3 texCoords;
float depthBias;
float depth;
float nDotL;
vec2 texelStepSize;
float normalShadingSmooth;
float darkness;
};
float czm_shadowVisibility(samplerCube shadowMap, zx_shadowParameters shadowParameters)
{
float depthBias = shadowParameters.depthBias;
float depth = shadowParameters.depth;
float nDotL = shadowParameters.nDotL;
float normalShadingSmooth = shadowParameters.normalShadingSmooth;
float darkness = shadowParameters.darkness;
vec3 uvw = shadowParameters.texCoords;
depth -= depthBias;
float visibility = czm_shadowDepthCompare(shadowMap, uvw, depth);
return czm_private_shadowVisibility(visibility, nDotL, normalShadingSmooth, darkness);
}
vec4 getPositionEC(){
return czm_windowToEyeCoordinates(gl_FragCoord);
}
vec3 getNormalEC(){
return vec3(1.);
}
vec4 toEye(in vec2 uv,in float depth){
vec2 xy=vec2((uv.x*2.-1.),(uv.y*2.-1.));
vec4 posInCamera=czm_inverseProjection*vec4(xy,depth,1.);
posInCamera=posInCamera/posInCamera.w;
return posInCamera;
}
vec3 pointProjectOnPlane(in vec3 planeNormal,in vec3 planeOrigin,in vec3 point){
vec3 v01=point-planeOrigin;
float d=dot(planeNormal,v01);
return(point-planeNormal*d);
}
float getDepth(in vec4 depth){
float z_window=czm_unpackDepth(depth);
z_window=czm_reverseLogDepth(z_window);
float n_range=czm_depthRange.near;
float f_range=czm_depthRange.far;
return(2.*z_window-n_range-f_range)/(f_range-n_range);
}
float shadow(in vec4 positionEC){
vec3 normalEC=getNormalEC();
zx_shadowParameters shadowParameters;
shadowParameters.texelStepSize=shadowMap_texelSizeDepthBiasAndNormalShadingSmooth.xy;
shadowParameters.depthBias=shadowMap_texelSizeDepthBiasAndNormalShadingSmooth.z;
shadowParameters.normalShadingSmooth=shadowMap_texelSizeDepthBiasAndNormalShadingSmooth.w;
shadowParameters.darkness=shadowMap_normalOffsetScaleDistanceMaxDistanceAndDarkness.w;
vec3 directionEC=positionEC.xyz-shadowMap_lightPositionEC.xyz;
float distance=length(directionEC);
directionEC=normalize(directionEC);
float radius=shadowMap_lightPositionEC.w;
if(distance>radius)
{
return 2.0;
}
vec3 directionWC=czm_inverseViewRotation*directionEC;
shadowParameters.depth=distance/radius-0.0003;
shadowParameters.nDotL=clamp(dot(normalEC,-directionEC),0.,1.);
shadowParameters.texCoords=directionWC;
float visibility=czm_shadowVisibility(shadowMap_textureCube,shadowParameters);
return visibility;
}
bool visible(in vec4 result)
{
result.x/=result.w;
result.y/=result.w;
result.z/=result.w;
return result.x>=-1.&&result.x<=1.
&&result.y>=-1.&&result.y<=1.
&&result.z>=-1.&&result.z<=1.;
}
void main(){
// 釉色 = 结构二维(颜色纹理, 纹理坐标)
gl_FragColor = texture2D(colorTexture, v_textureCoordinates);
// 深度 = 获取深度(结构二维(深度纹理, 纹理坐标))
float depth = getDepth(texture2D(depthTexture, v_textureCoordinates));
// 视角 = (纹理坐标, 深度)
vec4 viewPos = toEye(v_textureCoordinates, depth);
// 世界坐标
vec4 wordPos = czm_inverseView * viewPos;
// 虚拟相机中坐标
vec4 vcPos = camera_view_matrix * wordPos;
float near = .001 * helsing_viewDistance;
float dis = length(vcPos.xyz);
if(dis > near && dis < helsing_viewDistance){
// 透视投影
vec4 posInEye = camera_projection_matrix * vcPos;
// 可视区颜色
// vec4 helsing_visibleAreaColor=vec4(0.,1.,0.,.5);
// vec4 helsing_invisibleAreaColor=vec4(1.,0.,0.,.5);
if(visible(posInEye)){
float vis = shadow(viewPos);
if(vis > 0.3){
// gl_FragColor = mix(gl_FragColor,helsing_visibleAreaColor,.5);
} else {
gl_FragColor = mix(gl_FragColor,helsing_invisibleAreaColor,.5);
}
}
}
}`;

View File

@ -0,0 +1,302 @@
// ViewShed.js
import glsl from './glsl'
import Event from "../../../Event";
import MouseTip from "../../../MouseTip";
import Tools from "../../../Tools";
import Controller from "../../../Controller";
import Dialog from '../../../BaseDialog';
import { html } from "./_element";
/**
* @constructor
* @description 圆形可视域分析(--方块)
* @param sdk
* @param {Object} options 选项。
* @param {String} options.visibleAreaColor=#008000 可视区域颜色(默认值`绿色`)。
* @param {String} options.invisibleAreaColor=#FF0000 不可视区域颜色(默认值`红色`)。
*/
class ViewShedStage extends Tools {
constructor(sdk, options = {}, _Dialog = {}) {
super(sdk, options)
this.viewer = sdk.viewer;
this.options = {}
let precision = Math.floor(options.precision)
if (isNaN(precision)) {
this.precision = 40
}
else if (precision < 10) {
this.precision = 10
}
else {
this.precision = precision
}
this.options.visibleAreaColor = options.visibleAreaColor || '#008000';
this.options.invisibleAreaColor = options.invisibleAreaColor || '#FF0000';
this.ids = []
this.primitives = []
this.viewpointPrimitive = null
this.Dialog = _Dialog
this.html = null
YJ.Analysis.Analyses.push(this)
ViewShedStage.create(this)
}
static create(that) {
let count = 0;
if (!YJ.Measure.GetMeasureStatus()) {
let Draw = new YJ.Draw.DrawCircle(that.sdk)
Draw.start((a, options) => {
that.center = options.center
that.radius = options.radius
that.analyse()
})
}
else {
console.log('上一次测量未结束')
}
}
static async edit(that) {
if (that._DialogObject && that._DialogObject.close) {
that._DialogObject.close()
that._DialogObject = null
}
that._DialogObject = await new Dialog(that.sdk.viewer._container, {
title: '可视域分析', left: '180px', top: '100px',
closeCallBack: () => {
that.Dialog.closeCallBack && that.Dialog.closeCallBack()
YJ.Measure.SetMeasureStatus(false)
},
})
await that._DialogObject.init()
that._DialogObject._element.body.className = that._DialogObject._element.body.className + ' view-shed'
let contentElm = document.createElement('div');
contentElm.innerHTML = html()
that._DialogObject.contentAppChild(contentElm)
}
analyse() {
this.destroy()
let center = [this.center.lng, this.center.lat];
let radius = this.radius / 1000;
let circle = turf.circle(center, radius, { steps: 180, units: 'kilometers', properties: { foo: 'bar' } });
let pointPrimitives = null;// 申明点渲染集合
this.viewpointPrimitive = this.viewer.scene.primitives.add(new Cesium.PointPrimitiveCollection());
console.log('circle', circle)
let bbox = turf.bbox(circle);
console.log(bbox)
let bboxPolygon = turf.bboxPolygon(bbox);
console.log(bboxPolygon)
let cellSide = radius / this.precision;
let grid = turf.pointGrid(bbox, cellSide, { units: 'kilometers' });
console.log(grid)
let ptsWithin = turf.pointsWithinPolygon(grid, circle);
console.log('ptsWithin', ptsWithin)
let viewPoint = Cesium.Cartesian3.fromDegrees(this.center.lng, this.center.lat, this.center.alt + 2);
this.viewpointPrimitive.add({
position: viewPoint,
color: Cesium.Color.AQUA.withAlpha(1),
pixelSize: 6
})
let instances = []
let xp = (bbox[2] - bbox[0]) / (this.precision * 4)
let yp = (bbox[3] - bbox[1]) / (this.precision * 4)
let _this = this
let item = 200
let m = 0
let total = ptsWithin.features.length
let intervalEvent = setInterval(() => {
if (m >= ptsWithin.features.length) {
clearInterval(intervalEvent)
return
}
else {
InBatches(m)
m += 200
}
}, 30);
function InBatches(k) {
let instances = []
let length = k + 200
if (length >= ptsWithin.features.length) {
length = ptsWithin.features.length
}
for (let i = k; i < length; i++) {
let positionArr = []
let pt = ptsWithin.features[i].geometry.coordinates;
let cartographic = Cesium.Cartographic.fromDegrees(pt[0], pt[1]);
let height = _this.viewer.scene.globe.getHeight(cartographic)
let pt3d = Cesium.Cartesian3.fromDegrees(pt[0], pt[1], height);
// let position = this.viewer.scene.clampToHeight(pt3d)
let targetPoint = pt3d;
let direction = Cesium.Cartesian3.normalize(
Cesium.Cartesian3.subtract(
targetPoint,
viewPoint,
new Cesium.Cartesian3()
),
new Cesium.Cartesian3()
);
let ray = new Cesium.Ray(viewPoint, direction);
let pickedObjects = _this.viewer.scene.drillPickFromRay(ray);
let result
for (let i = 0; i < pickedObjects.length; i++) {
if (pickedObjects[i].position) {
result = pickedObjects[i]
break
}
}
let color = Cesium.Color.LIME
if (result && Math.abs(result.position.x - pt3d.x) > 0.01 && Math.abs(result.position.y - pt3d.y) > 0.01 && Math.abs(result.position.z - pt3d.z) > 0.01) {
color = Cesium.Color.RED
}
positionArr.push(pt[0] - xp, pt[1] + yp, pt[0] + xp, pt[1] + yp, pt[0] + xp, pt[1] - yp, pt[0] - xp, pt[1] - yp)
let polygon = new Cesium.PolygonGeometry({
polygonHierarchy: new Cesium.PolygonHierarchy(
Cesium.Cartesian3.fromDegreesArray(positionArr)
),
height: 0.0,
extrudedHeight: 0.0,
vertexFormat: Cesium.PerInstanceColorAppearance.VERTEX_FORMAT,
ellipsoid: Cesium.Ellipsoid.WGS84,
granularity: Cesium.Math.RADIANS_PER_DEGREE, // 每个纬度和经度之间的距离(以弧度为单位),确定缓冲区中的位置数
perPositionHeight: false, // 每个位置点使用的高度
closeTop: true,
closeBottom: true,
});
let polygonInstance = new Cesium.GeometryInstance({
geometry: polygon,
name: "ViewershedPolygon",
attributes: {
color: Cesium.ColorGeometryInstanceAttribute.fromColor(
color.withAlpha(0.6)
),
show: new Cesium.ShowGeometryInstanceAttribute(true), //显示或者隐藏
},
});
instances.push(polygonInstance)
}
_this.primitives.push(_this.viewer.scene.primitives.add(
new Cesium.GroundPrimitive({
geometryInstances: instances,
appearance: new Cesium.PerInstanceColorAppearance({
translucent: true, //false时透明度无效
closed: false,
}),
})
));
}
// for (let i = 0; i < ptsWithin.features.length; i++) {
// let positionArr = []
// let pt = ptsWithin.features[i].geometry.coordinates;
// let cartographic = Cesium.Cartographic.fromDegrees(pt[0], pt[1]);
// let height = this.viewer.scene.globe.getHeight(cartographic)
// let pt3d = Cesium.Cartesian3.fromDegrees(pt[0], pt[1], height);
// // let position = this.viewer.scene.clampToHeight(pt3d)
// let targetPoint = pt3d;
// let direction = Cesium.Cartesian3.normalize(
// Cesium.Cartesian3.subtract(
// targetPoint,
// viewPoint,
// new Cesium.Cartesian3()
// ),
// new Cesium.Cartesian3()
// );
// let ray = new Cesium.Ray(viewPoint, direction);
// let pickedObjects = this.viewer.scene.drillPickFromRay(ray);
// let result
// for (let i = 0; i < pickedObjects.length; i++) {
// if (pickedObjects[i].position) {
// result = pickedObjects[i]
// break
// }
// }
// let color = Cesium.Color.LIME
// if (result && Math.abs(result.position.x - pt3d.x) > 1 && Math.abs(result.position.y - pt3d.y) > 1 && Math.abs(result.position.z - pt3d.z) > 1) {
// color = Cesium.Color.RED
// // this.viewer.entities.add({
// // polyline: {
// // positions: [viewPoint, result.position],
// // material: Cesium.Color.GREEN.withAlpha(0.1),
// // // clampToGround: true,
// // width: 1,
// // zIndex: 99999999
// // },
// // });
// // this.viewer.entities.add({
// // polyline: {
// // positions: [result.position, targetPoint],
// // material: Cesium.Color.RED.withAlpha(0.1),
// // // clampToGround: true,
// // width: 1,
// // zIndex: 99999999
// // },
// // });
// // pointPrimitives.add({
// // position: result.position,
// // color: Cesium.Color.AQUA.withAlpha(0.5),
// // pixelSize: 6
// // })
// }
// positionArr.push(pt[0] - xp, pt[1] + yp, pt[0] + xp, pt[1] + yp, pt[0] + xp, pt[1] - yp, pt[0] - xp, pt[1] - yp)
// let polygon = new Cesium.PolygonGeometry({
// polygonHierarchy: new Cesium.PolygonHierarchy(
// Cesium.Cartesian3.fromDegreesArray(positionArr)
// ),
// height: 0.0,
// extrudedHeight: 0.0,
// vertexFormat: Cesium.PerInstanceColorAppearance.VERTEX_FORMAT,
// ellipsoid: Cesium.Ellipsoid.WGS84,
// granularity: Cesium.Math.RADIANS_PER_DEGREE, // 每个纬度和经度之间的距离(以弧度为单位),确定缓冲区中的位置数
// perPositionHeight: false, // 每个位置点使用的高度
// closeTop: true,
// closeBottom: true,
// });
// let polygonInstance = new Cesium.GeometryInstance({
// geometry: polygon,
// name: "ViewershedPolygon",
// attributes: {
// color: Cesium.ColorGeometryInstanceAttribute.fromColor(
// color.withAlpha(0.5)
// ),
// show: new Cesium.ShowGeometryInstanceAttribute(true), //显示或者隐藏
// },
// });
// instances.push(polygonInstance)
// }
// this.viewer.scene.primitives.add(
// new Cesium.GroundPrimitive({
// geometryInstances: instances,
// appearance: new Cesium.PerInstanceColorAppearance({
// translucent: true, //false时透明度无效
// closed: false,
// }),
// })
// );
}
destroy() {
for (let i = 0; i < this.primitives.length; i++) {
this.viewer.scene.primitives.remove(this.primitives[i])
}
this.primitives = []
this.viewpointPrimitive && this.viewer.scene.primitives.remove(this.viewpointPrimitive)
YJ.Measure.SetMeasureStatus(false)
}
}
export default ViewShedStage;

View File

@ -0,0 +1,19 @@
function html() {
return `
<span class="custom-divider"></span>
<div class="div-item">
<div class="row">
<div class="col">
<span class="label">视点高度</span>
<div class="input-number input-number-unit-1">
<input class="input" type="number" title="" min="0" max="999999" step="0.1" @model="viewPointHeight">
<span class="unit">m</span>
<span class="arrow"></span>
</div>
</div>
</div>
</div>
`
}
export { html }

View File

@ -0,0 +1,355 @@
/*
*通视分析
* @Author: Wang jianLei
* @Date: 2022-04-17 22:04:52
* @Last Modified by: Wang JianLei
* @Last Modified time: 2022-04-17 22:05:13
*/
import Tools from "../../../Tools";
import Event from "../../../Event";
import MouseTip from "../../../MouseTip";
import EventBinding from '../../Element/Dialog/eventBinding';
import Dialog from '../../../BaseDialog';
import { html } from "./_element";
class VisibilityAnalysis extends Tools {
constructor(sdk, options = {}, _Dialog = {}) {
super(sdk, options)
this.viewer = sdk.viewer;
this.resultObject = {
viewPoint: undefined, //通视分析起点
targetPoints: [], //通视分析目标点集合
targetPoint: undefined, //当前目标点
objectExclude: [], //射线排除集合
entities: [], //创建的Entity对象
};
this.options = {}
this._elms = {};
this.viewPointHeight = options.viewPointHeight
this.Dialog = _Dialog
this._EventBinding = new EventBinding()
YJ.Analysis.Analyses.push(this)
VisibilityAnalysis.edit(this)
}
get viewPointHeight() {
return this.options.viewPointHeight
}
set viewPointHeight(v) {
let viewPointHeight = Math.floor(Number(v) * 10) / 10
if (isNaN(viewPointHeight)) {
viewPointHeight = 1.8
}
if (viewPointHeight < 0) {
viewPointHeight = 0
}
this.options.viewPointHeight = viewPointHeight
this._elms.viewPointHeight && this._elms.viewPointHeight.forEach((item) => {
item.value = viewPointHeight
})
}
static create(that) {
if (!YJ.Measure.GetMeasureStatus()) {
if (that._DialogObject && that._DialogObject.close) {
that._DialogObject.close()
that._DialogObject = null
}
that.event = new Event(that.sdk)
that.tip = new MouseTip('左键点击创建视角起点', that.sdk)
YJ.Measure.SetMeasureStatus(true)
let count = 0;
that.event.mouse_left(async (movement, cartesian) => {
that.tip.set_text("左键创建视角终点,右键结束通视分析")
if (!that.resultObject.viewPoint) {
let pos84 = that.cartesian3Towgs84(cartesian, that.viewer)
let positions = await Cesium.sampleTerrainMostDetailed(
that.sdk.viewer.terrainProvider,
[Cesium.Cartographic.fromDegrees(pos84.lng, pos84.lat)]
);
if (positions[0].height > pos84.alt) {
pos84.alt = positions[0].height
}
pos84.alt = pos84.alt + that.viewPointHeight
let pos = Cesium.Cartesian3.fromDegrees(pos84.lng, pos84.lat, pos84.alt)
that.resultObject.viewPoint = pos;
let pointEntity = that.viewer.entities.add({
position: pos,
point: {
color: Cesium.Color.YELLOW,
pixelSize: 5,
},
});
that.resultObject.objectExclude.push(pointEntity);
that.resultObject.entities.push(pointEntity);
} else {
that.resultObject.targetPoint = cartesian;
let pointEntity = that.viewer.entities.add({
position: cartesian,
point: {
color: Cesium.Color.YELLOW,
pixelSize: 5,
},
});
that.resultObject.objectExclude.push(pointEntity);
that.resultObject.entities.push(pointEntity);
let direction = Cesium.Cartesian3.normalize(
Cesium.Cartesian3.subtract(
that.resultObject.targetPoint,
that.resultObject.viewPoint,
new Cesium.Cartesian3()
),
new Cesium.Cartesian3()
);
let ray = new Cesium.Ray(that.resultObject.viewPoint, direction);
let pickedObjects = that.viewer.scene.drillPickFromRay(ray);
let result = {}
for (let i = 0; i < pickedObjects.length; i++) {
if (pickedObjects[i].position) {
result = pickedObjects[i]
break
}
}
// let result = that.viewer.scene.pickFromRay(
// ray,
// that.resultObject.objectExclude
// ); // 计算交互点,返回第一个
if (result) {
let dis0 = VisibilityAnalysis.distance(
that.resultObject.viewPoint,
that.resultObject.targetPoint
);
let dis1 = VisibilityAnalysis.distance(
that.resultObject.viewPoint,
result.position || cartesian
);
let dis2 = VisibilityAnalysis.distance(
result.position || cartesian,
that.resultObject.targetPoint
);
if (dis0 > dis1) {
let _poly0 = that.viewer.entities.add({
polyline: {
positions: [that.resultObject.viewPoint, result.position],
material: Cesium.Color.GREEN,
width: 3,
zIndex: 99999999
},
});
that.resultObject.entities.push(_poly0);
let _poly1 = that.viewer.entities.add({
polyline: {
positions: [result.position, that.resultObject.targetPoint],
material: Cesium.Color.RED,
width: 3,
zIndex: 99999999
},
});
that.resultObject.entities.push(_poly1);
that.resultObject.targetPoints.push({
targetPoint: cartesian,
visual: false, //如果dis2足够小其实他是可视的
distance: [dis0, dis1, dis2], //[初始点和终点,初始点和交点,交点和终点]
});
} else {
let _poly2 = that.viewer.entities.add({
polyline: {
positions: [
that.resultObject.viewPoint,
that.resultObject.targetPoint,
],
material: Cesium.Color.GREEN,
width: 3,
zIndex: 99999999
},
});
that.resultObject.entities.push(_poly2);
that.resultObject.targetPoints.push({
targetPoint: cartesian,
visual: true, //如果dis2足够小其实他是可视的
distance: [dis0, dis1, dis2], //[初始点和终点,初始点和交点,交点和终点]
});
}
}
}
})
that.event.mouse_move((movement, cartesian) => {
that.tip.setPosition(cartesian, movement.endPosition.x, movement.endPosition.y)
})
that.event.mouse_right((movement, cartesian) => {
end()
})
that.event.gesture_pinck_start((movement, cartesian) => {
let startTime = new Date()
that.event.gesture_pinck_end(() => {
let endTime = new Date()
if (endTime - startTime >= 500) {
end()
}
})
})
}
else {
console.log('上一次测量未结束')
}
function end() {
YJ.Measure.SetMeasureStatus(false)
that.tip.destroy()
that.event.destroy()
that.tip = null
that.event = null
}
}
// static update(that) {
// if (!that.resultObject.viewPoint) {
// return
// }
// for (let i = that.resultObject.entities.length - 1; i >= 0; i--) {
// if (that.resultObject.entities[i].point) {
// that.viewer.entities.remove(that.resultObject.entities[i]);
// that.resultObject.entities.splice(i, 1)
// }
// }
// setTimeout(() => {
// for (let i = 0; i < that.resultObject.targetPoints.length; i++) {
// that.resultObject.targetPoint = that.resultObject.targetPoints[i].targetPoint;
// let direction = Cesium.Cartesian3.normalize(
// Cesium.Cartesian3.subtract(
// that.resultObject.targetPoint,
// that.resultObject.viewPoint,
// new Cesium.Cartesian3()
// ),
// new Cesium.Cartesian3()
// );
// let ray = new Cesium.Ray(that.resultObject.viewPoint, direction);
// let pickedObjects = that.viewer.scene.drillPickFromRay(ray);
// let result = {}
// for (let i = 0; i < pickedObjects.length; i++) {
// if (pickedObjects[i].position) {
// result = pickedObjects[i]
// break
// }
// }
// // let result = that.viewer.scene.pickFromRay(
// // ray,
// // that.resultObject.objectExclude
// // ); // 计算交互点,返回第一个
// if (result) {
// let dis0 = VisibilityAnalysis.distance(
// that.resultObject.viewPoint,
// that.resultObject.targetPoint
// );
// let dis1 = VisibilityAnalysis.distance(
// that.resultObject.viewPoint,
// result.position || cartesian
// );
// let dis2 = VisibilityAnalysis.distance(
// result.position || cartesian,
// that.resultObject.targetPoint
// );
// if (dis0 > dis1) {
// let _poly0 = that.viewer.entities.add({
// polyline: {
// positions: [that.resultObject.viewPoint, result.position],
// material: Cesium.Color.GREEN,
// width: 3,
// zIndex: 99999999
// },
// });
// that.resultObject.entities.push(_poly0);
// let _poly1 = that.viewer.entities.add({
// polyline: {
// positions: [result.position, that.resultObject.targetPoint],
// material: Cesium.Color.RED,
// width: 3,
// zIndex: 99999999
// },
// });
// that.resultObject.entities.push(_poly1);
// } else {
// let _poly2 = that.viewer.entities.add({
// polyline: {
// positions: [
// that.resultObject.viewPoint,
// that.resultObject.targetPoint,
// ],
// material: Cesium.Color.GREEN,
// width: 3,
// zIndex: 99999999
// },
// });
// that.resultObject.entities.push(_poly2);
// }
// }
// }
// }, 1000);
// }
static async edit(that) {
if (that._DialogObject && that._DialogObject.close) {
that._DialogObject.close()
that._DialogObject = null
}
that._DialogObject = await new Dialog(that.sdk.viewer._container, {
title: '多点视线分析', left: '180px', top: '100px',
closeCallBack: () => {
that.Dialog.closeCallBack && that.Dialog.closeCallBack()
YJ.Measure.SetMeasureStatus(false)
},
})
await that._DialogObject.init()
that._DialogObject._element.body.className = that._DialogObject._element.body.className + ' visibility'
let contentElm = document.createElement('div');
contentElm.innerHTML = html()
that._DialogObject.contentAppChild(contentElm)
let drawElm = document.createElement('button')
drawElm.innerHTML = '绘制'
drawElm.addEventListener('click', () => {
VisibilityAnalysis.create(that)
})
that._DialogObject.footAppChild(drawElm)
let all_elm = contentElm.getElementsByTagName("*")
that._EventBinding.on(that, all_elm)
that._elms = that._EventBinding.element
}
//空间两点间距离
static distance(point1, point2) {
let point1cartographic = Cesium.Cartographic.fromCartesian(point1);
let point2cartographic = Cesium.Cartographic.fromCartesian(point2);
/**根据经纬度计算出距离**/
let geodesic = new Cesium.EllipsoidGeodesic();
geodesic.setEndPoints(point1cartographic, point2cartographic);
let s = geodesic.surfaceDistance;
//返回两点之间的距离
s = Math.sqrt(
Math.pow(s, 2) +
Math.pow(point2cartographic.height - point1cartographic.height, 2)
);
return s;
}
destroy() {
this.resultObject.entities.forEach((element) => {
this.viewer.entities.remove(element);
});
this.resultObject = {
viewPoint: undefined, //通视分析起点
targetPoints: [], //通视分析目标点集合
targetPoint: undefined, //当前目标点
objectExclude: [], //射线排除集合
entities: [], //创建的Entity对象
};
this.tip && this.tip.destroy()
this.event && this.event.destroy()
this.tip = null
this.event = null
YJ.Measure.SetMeasureStatus(false)
}
}
export default VisibilityAnalysis;

View File

@ -0,0 +1,8 @@
function Clear() {
YJ.Analysis.Analyses.forEach(m => {
m.destroy()
})
// YJ.Analysis.Analyses = []
}
export {Clear}

View File

@ -0,0 +1,74 @@
function html() {
return `
<span class="custom-divider"></span>
<div class="div-item">
<div class="row">
<div class="col">
<span class="label">位置拾取(起点、终点)</span>
<button class="edit"><svg class="icon-edit"><use xlink:href="#yj-icon-edit"></use></svg>拾取</button>
</div>
</div>
</div>
<span class="custom-divider"></span>
<div class="div-item">
<div class="row subtitle-box">
<span class="subtitle">视域夹角</span>
</div>
<div class="row">
<div class="col">
<div class="range-box">
<div class="range-bg">
<div class="range-process-box">
<div class="range-process"></div>
</div>
</div>
<div class="range-node-box">
<span class="range-node-text">0°</span>
<span class="range-node-text">45°</span>
<span class="range-node-text">90°</span>
<span class="range-node-text">135°</span>
<span class="range-node-text">180°</span>
<div class="range-node-active"><span class="range-node-active-text">0°</span></div>
</div>
<input type="range" max="180" min="0" step="1" name="horizontalViewAngle">
</div>
</div>
</div>
</div>
<span class="custom-divider"></span>
<div class="div-item">
<div class="row">
<div class="col">
<span class="label">经度:</span>
<span class="text-number" name="lng"></span>
</div>
<div class="col">
<span class="label">偏航角:</span>
<span class="text-number" name="viewHeading"></span>
<span class="unit">°</span>
</div>
</div>
<div class="row">
<div class="col">
<span class="label">纬度:</span>
<span class="text-number" name="lat"></span>
</div>
<div class="col">
<span class="label">俯仰角:</span>
<span class="text-number" name="viewPitch"></span>
<span class="unit">°</span>
</div>
</div>
<div class="row">
<div class="col">
<span class="label">高度:</span>
<span class="text-number" name="alt"></span>
<span class="unit text-number" style="margin-left: 5px;">m</span>
</div>
</div>
</div>
<span class="custom-divider"></span>
`
}
export { html }

View File

@ -0,0 +1,511 @@
/*
* @Author: Wang jianLei
* @Date: 2022-05-17 21:49:28
* @Last Modified by: Wang JianLei
* @Last Modified time: 2022-05-19 22:08:14
*/
let ViewShed = function (sdk, canvasEleId) {
if (!sdk.viewer) throw new Error("no viewer object!");
let canvasEle = document.getElementById(canvasEleId);
if (!canvasEle) throw new Error("the canvas element is not exist");
this.canvasEle = canvasEle;
this.viewer = sdk.viewer;
this.handler = undefined;
this.lightCamera;
this.pyramid;
this.frustumPrimitive;
this.viewershedPolygon;
};
ViewShed.prototype = {
/**
* 初始化handler
*/
initHandler() {
if (this.handler) {
this.handler.destroy();
this.handler = undefined;
}
},
/**
* 开始执行视域分析
* @param {number} precision 精度值越大创建耗时越长建议在10~20之间
*/
createViewshed: function (precision) {
let $this = this;
let scene = $this.viewer.scene;
$this.initHandler();
$this.clearAll();
$this.handler = new Cesium.ScreenSpaceEventHandler($this.viewer.canvas);
$this.handler.setInputAction((event) => {
// 禁止地球旋转和缩放,地球的旋转会对鼠标移动监听有影响,所以需要禁止
scene.screenSpaceCameraController.enableRotate = false;
scene.screenSpaceCameraController.enableZoom = false;
scene.globe.depthTestAgainstTerrain = true;
let earthPosition = scene.pickPosition(event.position);
let pos = $this.cartesian3ToDegree(earthPosition);
$this.handler.setInputAction(function (event) {
let newPosition = scene.pickPosition(event.endPosition);
if (Cesium.defined(newPosition)) {
let pos1 = $this.cartesian3ToDegree(newPosition);
let distance = Cesium.Cartesian3.distance(newPosition, earthPosition);
let angle = $this.getAngle(pos[0], pos[1], pos1[0], pos1[1]);
let pitch = $this.getPitch(earthPosition, newPosition);
$this.ViewShedOptions = {
viewPosition: earthPosition, //观测点 笛卡尔坐标
endPosition: newPosition, //目标点 笛卡尔坐标
direction: angle, //观测方位角 默认为`0`,范围`0~360`
pitch: pitch, //俯仰角,radius,默认为`0`
horizontalViewAngle: 90, //可视域水平夹角,默认为 `90`,范围`0~360`
verticalViewAngle: 60, //可视域垂直夹角,默认为`60`,范围`0~180`
visibleAreaColor: Cesium.Color.GREEN, //可视区域颜色,默认为`green`
invisibleAreaColor: Cesium.Color.RED, //不可见区域颜色,默认为`red`
visualRange: distance, //距离,单位`米`
};
$this.updateViewShed();
}
}, Cesium.ScreenSpaceEventType.MOUSE_MOVE);
}, Cesium.ScreenSpaceEventType.LEFT_DOWN);
$this.handler.setInputAction(() => {
$this.initHandler();
// 开启地球旋转和缩放
scene.screenSpaceCameraController.enableRotate = true;
scene.screenSpaceCameraController.enableZoom = true;
$this.drawViewershed(precision);
}, Cesium.ScreenSpaceEventType.LEFT_UP);
},
ReturnDistance(pos0, pos1) {
let distance = 0;
let point1cartographic = Cesium.Cartographic.fromCartesian(pos0);
let point2cartographic = Cesium.Cartographic.fromCartesian(pos1);
/**根据经纬度计算出距离**/
let geodesic = new Cesium.EllipsoidGeodesic();
geodesic.setEndPoints(point1cartographic, point2cartographic);
let s = geodesic.surfaceDistance;
return s;
},
getHeight(x, y, objectsToExclude) {
let endCartographic = Cesium.Cartographic.fromDegrees(x, y);
let endHeight = this.viewer.scene.sampleHeight(
endCartographic,
objectsToExclude
);
return endHeight;
},
cartesian3ToDegree: function (Cartesian3) {
let _ellipsoid = this.viewer.scene.globe.ellipsoid;
let _cartographic = _ellipsoid.cartesianToCartographic(Cartesian3);
let _lat = Cesium.Math.toDegrees(_cartographic.latitude);
let _lng = Cesium.Math.toDegrees(_cartographic.longitude);
let _alt = _cartographic.height;
return [_lng, _lat, _alt];
},
getAngle: function (lng1, lat1, lng2, lat2) {
let dRotateAngle = Math.atan2(Math.abs(lng1 - lng2), Math.abs(lat1 - lat2));
if (lng2 >= lng1) {
dRotateAngle = lat2 < lat1 ? Math.PI - dRotateAngle : dRotateAngle;
} else {
dRotateAngle =
lat2 >= lat1 ? 2 * Math.PI - dRotateAngle : Math.PI + dRotateAngle;
}
dRotateAngle = (dRotateAngle * 180) / Math.PI;
return dRotateAngle;
},
getPitch(pointA, pointB) {
let transfrom = Cesium.Transforms.eastNorthUpToFixedFrame(pointA);
const vector = Cesium.Cartesian3.subtract(
pointB,
pointA,
new Cesium.Cartesian3()
);
let direction = Cesium.Matrix4.multiplyByPointAsVector(
Cesium.Matrix4.inverse(transfrom, transfrom),
vector,
vector
);
Cesium.Cartesian3.normalize(direction, direction);
return Cesium.Math.PI_OVER_TWO - Cesium.Math.acosClamped(direction.z);
},
updateViewShed: function () {
this.clear();
this.setLightCamera();
this.addVisualPyramid();
this.createFrustum();
},
clear: function () {
if (this.pyramid) {
this.viewer.entities.removeById(this.pyramid.id);
this.pyramid = undefined;
}
if (this.frustumPrimitive) {
this.viewer.scene.primitives.remove(this.frustumPrimitive);
this.frustumPrimitive = undefined;
}
if (this.debugModelMatrixPrimitive) {
this.viewer.scene.primitives.remove(this.debugModelMatrixPrimitive);
this.debugModelMatrixPrimitive = undefined;
}
},
clearAll: function () {
this.clear();
if (this.viewershedPolygon) {
this.viewer.scene.primitives.remove(this.viewershedPolygon);
this.viewershedPolygon = undefined;
}
},
addVisualPyramid: function () {
let options = this.ViewShedOptions;
let position = options.viewPosition;
let visualRange = Number(options.visualRange);
let transform = Cesium.Transforms.eastNorthUpToFixedFrame(position);
this.debugModelMatrixPrimitive = this.viewer.scene.primitives.add(
new Cesium.DebugModelMatrixPrimitive({
modelMatrix: transform,
length: 5.0,
})
);
const halfClock = options.horizontalViewAngle / 2;
const halfCone = options.verticalViewAngle / 2;
const pitch = Cesium.Math.toDegrees(options.pitch);
const ellipsoid = new Cesium.EllipsoidGraphics({
radii: new Cesium.Cartesian3(visualRange, visualRange, visualRange),
minimumClock: Cesium.Math.toRadians(90 - options.direction - halfClock),
maximumClock: Cesium.Math.toRadians(90 - options.direction + halfClock),
minimumCone: Cesium.Math.toRadians(90 - pitch - halfCone),
maximumCone: Cesium.Math.toRadians(90 - pitch + halfCone),
fill: false,
outline: true,
subdivisions: 256,
stackPartitions: 64,
slicePartitions: 64,
outlineColor: Cesium.Color.YELLOWGREEN.withAlpha(0.5),
});
const pyramidEntity = new Cesium.Entity({
position: position,
ellipsoid,
});
this.pyramid = this.viewer.entities.add(pyramidEntity);
},
setLightCamera: function () {
if (!this.lightCamera) {
this.lightCamera = new Cesium.Camera(this.viewer.scene);
}
let options = this.ViewShedOptions;
let visualRange = Number(options.visualRange);
this.lightCamera.position = options.viewPosition;
this.lightCamera.frustum.near = 0.1;
this.lightCamera.frustum.far = visualRange;
const hr = Cesium.Math.toRadians(options.horizontalViewAngle);
const vr = Cesium.Math.toRadians(options.verticalViewAngle);
this.lightCamera.frustum.aspectRatio =
(visualRange * Math.tan(hr / 2) * 2) /
(visualRange * Math.tan(vr / 2) * 2);
this.lightCamera.frustum.fov = hr > vr ? hr : vr;
this.lightCamera.setView({
destination: options.viewPosition,
orientation: {
heading: Cesium.Math.toRadians(options.direction || 0),
pitch: options.pitch || 0,
roll: 0,
},
});
},
createFrustum: function () {
const scratchRight = new Cesium.Cartesian3();
const scratchRotation = new Cesium.Matrix3();
const scratchOrientation = new Cesium.Quaternion();
const direction = this.lightCamera.directionWC;
const up = this.lightCamera.upWC;
let right = this.lightCamera.rightWC;
right = Cesium.Cartesian3.negate(right, scratchRight);
let rotation = scratchRotation;
Cesium.Matrix3.setColumn(rotation, 0, right, rotation);
Cesium.Matrix3.setColumn(rotation, 1, up, rotation);
Cesium.Matrix3.setColumn(rotation, 2, direction, rotation);
let orientation = Cesium.Quaternion.fromRotationMatrix(
rotation,
scratchOrientation
);
let instanceOutline = new Cesium.GeometryInstance({
geometry: new Cesium.FrustumOutlineGeometry({
frustum: this.lightCamera.frustum,
origin: this.ViewShedOptions.viewPosition,
orientation: orientation,
}),
id: "视椎体轮廓线" + Math.random().toString(36).substr(2),
attributes: {
color: Cesium.ColorGeometryInstanceAttribute.fromColor(
new Cesium.Color(0.0, 1.0, 0.0, 1.0)
),
show: new Cesium.ShowGeometryInstanceAttribute(true),
},
});
this.frustumPrimitive = this.viewer.scene.primitives.add(
new Cesium.Primitive({
geometryInstances: instanceOutline,
appearance: new Cesium.PerInstanceColorAppearance({
flat: true,
translucent: false,
closed: true,
}),
})
);
},
createPoint: function (firstPos, secondPos) {
let entity4FirstPos = new Cesium.Entity({
name: "firstPos",
show: true,
position: firstPos,
point: {
show: true,
pixelSize: 20,
color: Cesium.Color.RED,
outlineColor: Cesium.Color.YELLOW,
outlineWidth: 5,
},
description: `
<p>这是绘制的视椎体起点</p>`,
});
this.viewer.entities.add(entity4FirstPos);
let entity4SecondPos = new Cesium.Entity({
name: "secondPos",
show: true,
position: secondPos,
point: {
show: true,
pixelSize: 30,
color: Cesium.Color.YELLOW,
outlineColor: Cesium.Color.RED,
outlineWidth: 8,
},
description: `
<p>这是绘制的视椎体视角终点</p>`,
});
this.viewer.entities.add(entity4SecondPos);
},
//绘制可视域
add(positionArr) {
let polygon = new Cesium.PolygonGeometry({
polygonHierarchy: new Cesium.PolygonHierarchy(
Cesium.Cartesian3.fromDegreesArray(positionArr)
),
height: 0.0,
extrudedHeight: 0.0,
vertexFormat: Cesium.PerInstanceColorAppearance.VERTEX_FORMAT,
stRotation: 0.0, // 纹理的旋转坐标(以弧度为单位),正旋转是逆时针方向
ellipsoid: Cesium.Ellipsoid.WGS84,
granularity: Cesium.Math.RADIANS_PER_DEGREE, // 每个纬度和经度之间的距离(以弧度为单位),确定缓冲区中的位置数
perPositionHeight: false, // 每个位置点使用的高度
closeTop: true,
closeBottom: true,
// NONE 与椭圆表面不符的直线;GEODESIC 遵循测地路径;RHUMB 遵循大黄蜂或恶魔般的道路。
arcType: Cesium.ArcType.GEODESIC, // 多边形边缘线型
});
let polygonInstance = new Cesium.GeometryInstance({
geometry: polygon,
name: "ViewershedPolygon",
attributes: {
color: Cesium.ColorGeometryInstanceAttribute.fromColor(
Cesium.Color.BLUE.withAlpha(0.6)
),
show: new Cesium.ShowGeometryInstanceAttribute(true), //显示或者隐藏
},
});
this.viewershedPolygon = this.viewer.scene.primitives.add(
new Cesium.GroundPrimitive({
geometryInstances: polygonInstance,
appearance: new Cesium.EllipsoidSurfaceAppearance({
aboveGround: true,
material: new Cesium.Material({
fabric: {
type: "Image",
uniforms: {
image: this.returnImgae(),
},
},
}),
}),
})
);
},
drawViewershed(precision) {
const pos = this.cartesian3ToDegree(this.ViewShedOptions.viewPosition);
const radius = this.ViewShedOptions.visualRange;
const direction = this.ViewShedOptions.direction;
let boundary = this.computeBoundaryOptions(pos, radius, direction);
const bbox = boundary.bbox;
let mask = turf.polygon([boundary.boundaryPoints]);
const dis = this.ViewShedOptions.visualRange / (precision * 1000);
let gridPoints = turf.pointGrid(bbox, dis, { mask: mask });
let pointsResult = this.createTargetPoints(gridPoints, dis, pos);
let variogram = kriging.train(
pointsResult.values,
pointsResult.lngs,
pointsResult.lats,
"exponential",
0,
100
);
let grid = kriging.grid([boundary.boundaryPoints], variogram, dis / 1000);
const colors = [
"#ff000080",
"#ff000080",
"#ff000080",
"#ff000080",
"#ff000080",
"#ff000080",
"#00ff0080",
"#00ff0080",
"#00ff0080",
"#00ff0080",
"#00ff0080",
"#00ff0080",
];
this.canvasEle.width = 3840;
this.canvasEle.height = 2160;
kriging.plot(
this.canvasEle,
grid,
[bbox[0], bbox[2]],
[bbox[1], bbox[3]],
colors
);
this.add(boundary.positionArr);
},
computeBoundaryOptions(pos, radius, angle) {
let Ea = 6378137; // 赤道半径
let Eb = 6356725; // 极半径
const lng = pos[0],
lat = pos[1];
const bbox = [lng, lat, lng, lat]; //[minX, minY, maxX, maxY]
let positionArr = [];
let boundaryPoints = [];
positionArr.push(lng, lat);
boundaryPoints.push([lng, lat]);
//正北是0°
let start = angle + 45 > 360 ? angle - 45 - 360 : angle - 45;
let end = start + 90;
for (let i = start; i <= end; i++) {
let dx = radius * Math.sin((i * Math.PI) / 180.0);
let dy = radius * Math.cos((i * Math.PI) / 180.0);
let ec = Eb + ((Ea - Eb) * (90.0 - lat)) / 90.0;
let ed = ec * Math.cos((lat * Math.PI) / 180);
let BJD = lng + ((dx / ed) * 180.0) / Math.PI;
let BWD = lat + ((dy / ec) * 180.0) / Math.PI;
positionArr.push(BJD, BWD);
boundaryPoints.push([BJD, BWD]);
this.refreshBBox(bbox, BJD, BWD);
}
boundaryPoints.push([lng, lat]);
return {
positionArr,
boundaryPoints,
bbox,
};
},
/**
* 更新外围矩形 Bbox
* @param {Array} result 外围矩形Bbox-[minX, minY, maxX, maxY]
* @param {Number} x 经度
* @param {Number} y 纬度
*/
refreshBBox(result, x, y) {
result[0] = x < result[0] ? x : result[0];
result[1] = y < result[1] ? y : result[1];
result[2] = x > result[2] ? x : result[2];
result[3] = y > result[3] ? y : result[3];
},
/**
* 插值点用射线判断通视性
* @param {*} gridPoints 网格点
* @param {*} step 步长,可以理解成是精度
* @param {*} sourcePos 视域分析起点
* @returns kriging插值所需的参数对象{ values:[], lngs:[], lats:[]}
*/
createTargetPoints(gridPoints, step, sourcePos) {
let positionArr = [];
let objectsToExclude = [
this.frustumPrimitive,
this.pyramid,
this.debugModelMatrixPrimitive,
];
let values = [],
lngs = [],
lats = [];
let height = this.getHeight(sourcePos[0], sourcePos[1], objectsToExclude);
positionArr.push({
x: sourcePos[0],
y: sourcePos[1],
z: height,
});
let viewPoint = this.ViewShedOptions.viewPosition;
for (let index = 0; index < gridPoints.features.length; index++) {
const feature = gridPoints.features[index];
const coords = feature.geometry.coordinates;
const x = coords[0],
y = coords[1];
let h = this.getHeight(x, y, objectsToExclude);
let endPoint = Cesium.Cartesian3.fromDegrees(x, y, h);
let direction = Cesium.Cartesian3.normalize(
Cesium.Cartesian3.subtract(
endPoint,
viewPoint,
new Cesium.Cartesian3()
),
new Cesium.Cartesian3()
);
// 建立射线
let ray = new Cesium.Ray(viewPoint, direction);
let result = this.viewer.scene.pickFromRay(ray, objectsToExclude); // 计算交互点,返回第一个
if (result) {
let buffer = this.ReturnDistance(endPoint, result.position);
// let M_color = Cesium.Color.GREEN;
if (buffer > step) {
// M_color = Cesium.Color.RED;
values.push(0);
} else {
values.push(1);
}
lngs.push(x);
lats.push(y);
// this.viewer.entities.add(
// new Cesium.Entity({
// name: "插值点哦",
// show: true,
// position: endPoint,
// point: {
// show: true,
// pixelSize: 10,
// color: M_color,
// outlineWidth: 2,
// outlineColor: Cesium.Color.YELLOW,
// },
// })
// );
}
}
return {
values,
lngs,
lats,
};
},
/**
* canvas转image图片
* @returns base64图片
*/
returnImgae() {
return this.canvasEle.toDataURL("image/png");
},
};
export default ViewShed;

View File

@ -0,0 +1,72 @@
export default `
#define USE_CUBE_MAP_SHADOW true
uniform sampler2D colorTexture;
uniform sampler2D depthTexture;
varying vec2 v_textureCoordinates;
uniform mat4 camera_projection_matrix;
uniform mat4 camera_view_matrix;
uniform vec4 helsing_visibleAreaColor;
uniform vec4 helsing_invisibleAreaColor;
uniform sampler2D helsing_texture;
vec4 getPositionEC(){
return czm_windowToEyeCoordinates(gl_FragCoord);
}
vec3 getNormalEC(){
return vec3(1.);
}
vec4 toEye(in vec2 uv,in float depth){
vec2 xy=vec2((uv.x*2.-1.),(uv.y*2.-1.));
vec4 posInCamera=czm_inverseProjection*vec4(xy,depth,1.);
posInCamera=posInCamera/posInCamera.w;
return posInCamera;
}
vec3 pointProjectOnPlane(in vec3 planeNormal,in vec3 planeOrigin,in vec3 point){
vec3 v01=point-planeOrigin;
float d=dot(planeNormal,v01);
return(point-planeNormal*d);
}
float getDepth(in vec4 depth){
float z_window=czm_unpackDepth(depth);
z_window=czm_reverseLogDepth(z_window);
float n_range=czm_depthRange.near;
float f_range=czm_depthRange.far;
return(2.*z_window-n_range-f_range)/(f_range-n_range);
}
bool visible(in vec4 result)
{
result.x/=result.w;
result.y/=result.w;
result.z/=result.w;
return result.x>=-1.&&result.x<=1.
&&result.y>=-1.&&result.y<=1.
&&result.z>=-1.&&result.z<=1.;
}
void main(){
// 釉色 = 结构二维(颜色纹理, 纹理坐标)
gl_FragColor = texture2D(colorTexture, v_textureCoordinates);
// 深度 = 获取深度(结构二维(深度纹理, 纹理坐标))
float depth = getDepth(texture2D(depthTexture, v_textureCoordinates));
// 视角 = (纹理坐标, 深度)
vec4 positionEC = toEye(v_textureCoordinates, depth);
// 世界坐标
vec4 wordPos = czm_inverseView * positionEC;
// 虚拟相机中坐标
vec4 vcPos = camera_view_matrix * wordPos;
vec4 videoColor = texture2D(helsing_texture, v_textureCoordinates);
float dis = length(vcPos.xyz);
vec4 posInEye = camera_projection_matrix * vcPos;
// 可视区颜色
// vec4 helsing_visibleAreaColor=vec4(0.,1.,0.,.5);
// vec4 helsing_invisibleAreaColor=vec4(1.,0.,0.,.5);
if(visible(posInEye)){
vec4 out_FragColor = helsing_visibleAreaColor;
gl_FragColor = mix(gl_FragColor,videoColor,1.0);
}
}`;

View File

@ -0,0 +1,787 @@
// ViewShed.js
import glsl from './glsl'
import Event from "../../../Event";
import MouseTip from "../../../MouseTip";
import Tools from "../../../Tools";
import Controller from "../../../Controller";
import Dialog from '../../../BaseDialog';
import { html } from "./_element";
/**
* @constructor
* @description 可视域分析
* @param sdk
* @param {Object} options 选项。
* @param {Cesium.Cartesian3} options.viewPosition 观测点位置。
* @param {Cesium.Cartesian3} options.viewPositionEnd 最远观测点位置(如果设置了观测距离,这个属性可以不设置)。
* @param {Number} options.viewDistance 观测距离(单位`米`)。
* @param {Number} options.viewHeading 航向角(单位`度`)。
* @param {Number} options.viewPitch 俯仰角(单位`度`)。
* @param {Number} options.horizontalViewAngle=90 可视域水平夹角(单位`度`)。
* @param {Number} options.verticalViewAngle=60 可视域垂直夹角(单位`度`)。
* @param {String} options.visibleAreaColor=#008000 可视区域颜色(默认值`绿色`)。
* @param {String} options.invisibleAreaColor=#FF0000 不可视区域颜色(默认值`红色`)。
*/
class Test extends Tools {
constructor(sdk, options = {}, _Dialog = {}) {
super(sdk, options)
// if (Object.hasOwn(options.viewPosition, 'lng') && Object.hasOwn(options.viewPosition, 'lat') && Object.hasOwn(options.viewPosition, 'alt')) {
// this.error = '请提供观测点位置!'
// window.ELEMENT && window.ELEMENT.Message({
// message: '请提供观测点位置!',
// type: 'warning',
// duration: 1500
// });
// return
// }
this.viewer = sdk.viewer;
this.options = {}
this.options.viewPosition = options.viewPosition;
this.options.viewPositionEnd = options.viewPositionEnd;
this.options.horizontalViewAngle = (options.horizontalViewAngle || options.horizontalViewAngle === 0) ? options.horizontalViewAngle : 90.0;
this.options.verticalViewAngle = (options.verticalViewAngle || options.verticalViewAngle === 0) ? options.verticalViewAngle : 60.0;
this.options.visibleAreaColor = options.visibleAreaColor || '#008000';
this.options.invisibleAreaColor = options.invisibleAreaColor || '#FF0000';
// this.enabled = (typeof options.enabled === "boolean") ? options.enabled : true;
// this.softShadows = (typeof options.softShadows === "boolean") ? options.softShadows : true;
// this.size = options.size || 10240; // 2048
this.ids = []
this.Dialog = _Dialog
this.html = null
YJ.Analysis.Analyses.push(this)
Test.create(this)
// Test.edit(this)
// this.update();
}
get viewPosition() {
return this.options.viewPosition
}
set viewPosition(v) {
this.options.viewPosition = v
this.ids[0] && (this.viewer.entities.getById(this.ids[0]).position = new Cesium.Cartesian3.fromDegrees(v.lng, v.lat, v.alt))
this.update()
// let viewPosition3 = Cesium.Cartesian3.fromDegrees(this.options.viewPosition.lng, this.options.viewPosition.lat, this.options.viewPosition.alt)
}
get viewPositionEnd() {
return this.options.viewPositionEnd
}
set viewPositionEnd(v) {
this.options.viewPositionEnd = v
this.ids[1] && (this.viewer.entities.getById(this.ids[1]).position = new Cesium.Cartesian3.fromDegrees(v.lng, v.lat, v.alt))
this.update()
// let viewPositionEnd3 = Cesium.Cartesian3.fromDegrees(this.options.viewPositionEnd.lng, this.options.viewPositionEnd.lat, this.options.viewPositionEnd.alt)
// this.viewDistance = this.viewPositionEnd ? Cesium.Cartesian3.distance(this.viewPosition, this.viewPositionEnd) : (options.viewDistance || 100.0);
}
get horizontalViewAngle() {
return this.options.horizontalViewAngle
}
set horizontalViewAngle(v) {
this.options.horizontalViewAngle = v
if (this._DialogObject && this._DialogObject._element && this._DialogObject._element.content) {
let contentElm = this._DialogObject._element.content
let e_horizontalViewAngle = contentElm.querySelector("input[name='horizontalViewAngle']")
e_horizontalViewAngle.value = v
let rangeNodeActive = contentElm.getElementsByClassName('range-node-active')[0]
let rangeNodeActiveText = rangeNodeActive.getElementsByClassName('range-node-active-text')[0]
rangeNodeActiveText.innerHTML = v + '°';
let rangeProcess = contentElm.getElementsByClassName('range-process')[0]
rangeProcess.style.width = v / 180 * 100 + '%'
}
this.update()
}
get visibleAreaColor() {
return this.options.visibleAreaColor
}
set visibleAreaColor(v) {
this.options.visibleAreaColor = v
this.update()
}
get invisibleAreaColor() {
return this.options.invisibleAreaColor
}
set invisibleAreaColor(v) {
this.options.invisibleAreaColor = v
this.update()
}
get verticalViewAngle() {
return this.options.verticalViewAngle
}
set verticalViewAngle(v) {
this.options.verticalViewAngle = v
this.update()
}
get viewDistance() {
let viewPosition3 = Cesium.Cartesian3.fromDegrees(this.options.viewPosition.lng, this.options.viewPosition.lat, this.options.viewPosition.alt)
let viewPositionEnd3 = Cesium.Cartesian3.fromDegrees(this.options.viewPositionEnd.lng, this.options.viewPositionEnd.lat, this.options.viewPositionEnd.alt)
let distance = Cesium.Cartesian3.distance(viewPosition3, viewPositionEnd3)
return distance
}
get viewHeading() {
let viewPosition3 = Cesium.Cartesian3.fromDegrees(this.options.viewPosition.lng, this.options.viewPosition.lat, this.options.viewPosition.alt)
let viewPositionEnd3 = Cesium.Cartesian3.fromDegrees(this.options.viewPositionEnd.lng, this.options.viewPositionEnd.lat, this.options.viewPositionEnd.alt)
let heading = getHeading(viewPosition3, viewPositionEnd3)
if (this.html) {
let e_viewHeading = this.html.querySelector("span[name='viewHeading']")
e_viewHeading.innerHTML = Number(heading.toFixed(8))
}
return heading
}
get viewPitch() {
let viewPosition3 = Cesium.Cartesian3.fromDegrees(this.options.viewPosition.lng, this.options.viewPosition.lat, this.options.viewPosition.alt)
let viewPositionEnd3 = Cesium.Cartesian3.fromDegrees(this.options.viewPositionEnd.lng, this.options.viewPositionEnd.lat, this.options.viewPositionEnd.alt)
let pitch = getPitch(viewPosition3, viewPositionEnd3)
if (this.html) {
let e_viewPitch = this.html.querySelector("span[name='viewPitch']")
e_viewPitch.innerHTML = Number(pitch.toFixed(8))
}
return pitch
}
static create(that) {
let count = 0;
if (!YJ.Measure.GetMeasureStatus()) {
that.event = new Event(that.sdk)
that.tip = new MouseTip('左键选择观测点位置,右键取消', that.sdk)
YJ.Measure.SetMeasureStatus(true)
that.event.mouse_left((movement, cartesian) => {
if (!that.viewPosition) {
that.options.viewPosition = that.cartesian3Towgs84(cartesian, that.viewer)
that.ids.push(Test.create_point(that, cartesian))
that.tip.set_text("左键选择最远观测点位置,右键取消")
}
count++
if (count === 2) {
that.options.viewPositionEnd = that.cartesian3Towgs84(cartesian, that.viewer)
that.ids.push(Test.create_point(that, cartesian))
end()
that.update()
}
})
that.event.mouse_move((movement, cartesian) => {
that.tip.setPosition(cartesian, movement.endPosition.x, movement.endPosition.y)
})
that.event.mouse_right((movement, cartesian) => {
that.ids.forEach(id => {
that.viewer.entities.removeById(id)
})
that.ids = []
end()
})
that.event.gesture_pinck_start((movement, cartesian) => {
let startTime = new Date()
that.event.gesture_pinck_end(() => {
let endTime = new Date()
if (endTime - startTime >= 500) {
that.ids.forEach(id => {
that.viewer.entities.removeById(id)
})
that.ids = []
end()
}
})
})
}
else {
console.log('上一次测量未结束')
}
function end() {
that.ids.forEach(id => {
let entity = that.viewer.entities.getById(id)
entity.show = false
})
YJ.Measure.SetMeasureStatus(false)
that.tip.destroy()
that.event.destroy()
that.tip = null
that.event = null
if (count === 2) {
Test.edit(that)
}
}
}
static create_point(that, cartesian) {
let id = that.randomString()
let p = that.cartesian3Towgs84(cartesian, that.viewer)
let params = {
id: id,
position: Cesium.Cartesian3.fromDegrees(p.lng, p.lat, p.alt),
billboard: {
image: that.getSourceRootPath() + '/img/point.png',
verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
disableDepthTestDistance: Number.POSITIVE_INFINITY,
color: Cesium.Color.WHITE.withAlpha(0.99)
}
}
that.viewer.entities.add(
new Cesium.Entity(params)
)
return id
}
add() {
this.createLightCamera();
this.createShadowMap();
this.createPostStage();
this.drawFrustumOutline();
// this.drawSketch();
}
update() {
this.clear();
this.add();
}
static async edit(that) {
if (that._DialogObject && that._DialogObject.close) {
that._DialogObject.close()
that._DialogObject = null
}
that._DialogObject = await new Dialog(that.sdk.viewer._container, {
title: '可视域分析', left: '180px', top: '100px',
closeCallBack: () => {
that.Dialog.closeCallBack && that.Dialog.closeCallBack()
YJ.Measure.SetMeasureStatus(false)
that.editevent && that.editevent.destroy()
that.ControllerObject && that.ControllerObject.destroy()
that.ids.forEach(id => {
that.viewer.entities.removeById(id)
})
},
})
await that._DialogObject.init()
that._DialogObject._element.body.className = that._DialogObject._element.body.className + ' view-shed'
let contentElm = document.createElement('div');
contentElm.innerHTML = html()
that._DialogObject.contentAppChild(contentElm)
let resetBtn = that._DialogObject._element.body.getElementsByClassName('edit')[0];
resetBtn.addEventListener('click', () => {
that.nodeEdit()
})
that.html = contentElm
//经度值
let e_lng = contentElm.querySelector("span[name='lng']")
e_lng.innerHTML = Number(that.options.viewPosition.lng.toFixed(8))
//纬度值
let e_lat = contentElm.querySelector("span[name='lat']")
e_lat.innerHTML = Number(that.options.viewPosition.lat.toFixed(8))
//高度值
let e_alt = contentElm.querySelector("span[name='alt']")
e_alt.innerHTML = Number(that.options.viewPosition.alt.toFixed(8))
//偏航角
let e_viewHeading = contentElm.querySelector("span[name='viewHeading']")
e_viewHeading.innerHTML = Number(that.viewHeading.toFixed(8))
//俯仰角
let e_viewPitch = contentElm.querySelector("span[name='viewPitch']")
e_viewPitch.innerHTML = Number(that.viewPitch.toFixed(8))
//视域夹角
let e_horizontalViewAngle = contentElm.querySelector("input[name='horizontalViewAngle']")
e_horizontalViewAngle.value = that.options.horizontalViewAngle
let rangeNodeActive = contentElm.getElementsByClassName('range-node-active')[0]
let rangeNodeActiveText = rangeNodeActive.getElementsByClassName('range-node-active-text')[0]
let rangeProcess = contentElm.getElementsByClassName('range-process')[0]
let percentage = that.horizontalViewAngle / 180 * 100
rangeNodeActive.style.left = percentage + '%';
rangeProcess.style.width = percentage + '%'
rangeNodeActiveText.innerHTML = that.horizontalViewAngle + '°';
let timeout
e_horizontalViewAngle.addEventListener('input', () => {
let percentage = e_horizontalViewAngle.value / 180 * 100
rangeNodeActive.style.left = percentage + '%';
rangeProcess.style.width = percentage + '%';
rangeNodeActiveText.innerHTML = e_horizontalViewAngle.value + '°';
})
e_horizontalViewAngle.addEventListener('change', () => {
clearTimeout(timeout)
timeout = setTimeout(() => {
that.horizontalViewAngle = e_horizontalViewAngle.value;
}, 300);
});
}
clear() {
YJ.Measure.SetMeasureStatus(false)
this.tip && this.tip.destroy()
this.event && this.event.destroy()
this.tip = null
this.event = null
if (this.sketch) {
this.viewer.entities.removeById(this.sketch.id);
this.sketch = null;
}
if (this.frustumOutline) {
this.frustumOutline.destroy();
this.frustumOutline = null;
}
if (this.FrustumBottomSurface) {
this.FrustumBottomSurface.destroy();
this.FrustumBottomSurface = null;
}
if (this.postStage) {
this.viewer.scene.postProcessStages.remove(this.postStage);
this.postStage = null;
}
}
destroy() {
this.clear()
this.editevent && this.editevent.destroy()
this.ControllerObject && this.ControllerObject.destroy()
this.ids.forEach(id => {
this.viewer.entities.removeById(id)
})
YJ.Measure.SetMeasureStatus(false)
}
nodeEdit() {
if (YJ.Measure.GetMeasureStatus()) {
console.log('上一次测量未结束')
}
else {
this.editevent && this.editevent.destroy()
this.ids.forEach(id => {
let entity = this.viewer.entities.getById(id)
entity.show = true
})
let selectPoint
YJ.Measure.SetMeasureStatus(true)
// this.tip = new MouseTip('左键选择要操作的观测点,右键取消', this.sdk)
this.editevent = new Event(this.sdk)
this.editevent.mouse_left((movement, cartesian) => {
let pick = this.viewer.scene.pick(movement.position);
if (pick && pick.id && pick.id.id && this.ids.indexOf(pick.id.id) != -1 && (!selectPoint || selectPoint.id != pick.id.id)) {
selectPoint = pick.id
// this.event.destroy()
// this.tip.destroy()
this.viewer.entities.getById(this.ids[0]).position = new Cesium.Cartesian3.fromDegrees(this.viewPosition.lng, this.viewPosition.lat, this.viewPosition.alt)
this.viewer.entities.getById(this.ids[1]).position = new Cesium.Cartesian3.fromDegrees(this.viewPositionEnd.lng, this.viewPositionEnd.lat, this.viewPositionEnd.alt)
this.viewPosition
this.ControllerObject && this.ControllerObject.destroy()
console.log(this.cartesian3Towgs84(selectPoint.position._value, this.sdk.viewer))
this.ControllerObject = new Controller(this.sdk, { position: { ...this.cartesian3Towgs84(selectPoint.position._value, this.sdk.viewer) } })
this.ControllerObject.controllerCallBack = (params, status) => {
if (params.position.alt < 0) {
params.position.alt = 0
}
selectPoint.position = new Cesium.Cartesian3.fromDegrees(params.position.lng, params.position.lat, params.position.alt)
if (status) {
if (this.ids.indexOf(pick.id.id) == 0) {
this.viewPosition = params.position
}
else {
this.viewPositionEnd = params.position
}
YJ.Measure.SetMeasureStatus(true)
}
}
this.ControllerObject.editTranslational()
}
})
this.editevent.mouse_right((movement, cartesian) => {
YJ.Measure.SetMeasureStatus(false)
this.editevent && this.editevent.destroy()
this.ControllerObject && this.ControllerObject.destroy()
this.ids.forEach(id => {
let entity = this.viewer.entities.getById(id)
entity.show = false
})
selectPoint = null
})
this.editevent.mouse_move((movement, cartesian) => {
// this.tip.setPosition(
// cartesian,
// movement.endPosition.x,
// movement.endPosition.y
// )
})
this.editevent.gesture_pinck_start((movement, cartesian) => {
let startTime = new Date()
this.editevent.gesture_pinck_end(() => {
let endTime = new Date()
if (endTime - startTime >= 500) {
YJ.Measure.SetMeasureStatus(false)
this.editevent && this.editevent.destroy()
this.ControllerObject && this.ControllerObject.destroy()
this.ids.forEach(id => {
let entity = this.viewer.entities.getById(id)
entity.show = false
})
selectPoint = null
}
})
})
}
}
createLightCamera() {
let _this = this
this.lightCamera = new Cesium.Camera(this.viewer.scene);
this.lightCamera.position = Cesium.Cartesian3.fromDegrees(this.options.viewPosition.lng, this.options.viewPosition.lat, this.options.viewPosition.alt);
// if (this.viewPositionEnd) {
// let direction = Cesium.Cartesian3.normalize(Cesium.Cartesian3.subtract(this.viewPositionEnd, this.viewPosition, new Cesium.Cartesian3()), new Cesium.Cartesian3());
// this.lightCamera.direction = direction; // direction是相机面向的方向
// }
this.lightCamera.frustum.near = this.viewDistance * 0.001;
this.lightCamera.frustum.far = this.viewDistance;
const hr = Cesium.Math.toRadians(this.horizontalViewAngle);
const vr = Cesium.Math.toRadians(this.verticalViewAngle);
const aspectRatio =
(this.viewDistance * Math.tan(hr / 2) * 2) /
(this.viewDistance * Math.tan(vr / 2) * 2);
this.lightCamera.frustum.aspectRatio = aspectRatio;
if (hr > vr) {
this.lightCamera.frustum.fov = hr;
} else {
this.lightCamera.frustum.fov = vr;
}
this.lightCamera.setView({
destination: Cesium.Cartesian3.fromDegrees(this.options.viewPosition.lng, this.options.viewPosition.lat, this.options.viewPosition.alt),
orientation: {
heading: Cesium.Math.toRadians(this.viewHeading || 0),
pitch: Cesium.Math.toRadians(this.viewPitch || 0),
roll: 0
}
});
}
createShadowMap() {
this.shadowMap = new Cesium.ShadowMap({
context: (this.viewer.scene).context,
lightCamera: this.lightCamera,
enabled: true,
isPointLight: true,
pointLightRadius: this.viewDistance,
cascadesEnabled: false,
size: 2048, // 2048
softShadows: true,
normalOffset: false,
fromLightSource: false
});
this.viewer.scene.shadowMap = this.shadowMap;
}
createPostStage() {
const video = document.getElementsByTagName('video')[0];
const img = document.getElementById('img')[0];
const that = this;
if (video /*&& !video.paused*/) {
this.activeVideoListener || (this.activeVideoListener = function () {
that.texture && that.texture.destroy();
that.texture = new Cesium.Texture({
context: that.viewer.scene.context,
source: video
});
});
that.viewer.clock.onTick.addEventListener(this.activeVideoListener);
}
const fs = glsl
const postStage = new Cesium.PostProcessStage({
vertexShaderText: `
attribute vec3 position3DHigh;
attribute vec3 position3DLow;
attribute vec3 normal;
attribute vec2 st;
attribute float batchId;
uniform mat3 rotation;
uniform vec3 scale;
uniform vec3 translation;
uniform mat4 modelViewProjection;
varying vec2 v_st;
void main() {
v_st = st;
vec3 positionHigh = position3DHigh + normal * scale;
vec3 positionLow = position3DLow + normal * scale;
vec4 positionWC = vec4(positionHigh + (positionLow + translation) / 65536.0, 1.0);
gl_Position = modelViewProjection * positionWC;
}
`,
fragmentShader: `
precision highp float;
varying vec2 v_st;
uniform sampler2D colorTexture;
void main() {
vec2 stripeMultiplier = vec2(1.0, 1.0);
// 水平贴合效果
stripeMultiplier.y *= abs(fract(v_st.y * 100.0) - 0.5) * 2.0;
gl_FragColor = texture2D(colorTexture, v_st * stripeMultiplier);
}
`
,
uniforms: {
helsing_texture: function () {
return that.texture;
},
shadowMap_textureCube: () => {
this.shadowMap.update(Reflect.get(this.viewer.scene, "_frameState"));
return Reflect.get(this.shadowMap, "_shadowMapTexture");
},
shadowMap_matrix: () => {
this.shadowMap.update(Reflect.get(this.viewer.scene, "_frameState"));
return Reflect.get(this.shadowMap, "_shadowMapMatrix");
},
shadowMap_texture: () => {
this.shadowMap.update(Reflect.get(this.viewer.scene, "_frameState"));
return Reflect.get(this.shadowMap, "_shadowMapTexture");
},
shadowMap_lightPositionEC: () => {
this.shadowMap.update(Reflect.get(this.viewer.scene, "_frameState"));
return Reflect.get(this.shadowMap, "_lightPositionEC");
},
shadowMap_normalOffsetScaleDistanceMaxDistanceAndDarkness: () => {
this.shadowMap.update(Reflect.get(this.viewer.scene, "_frameState"));
const bias = this.shadowMap._pointBias;
return Cesium.Cartesian4.fromElements(
bias.normalOffsetScale,
this.shadowMap._distance,
this.shadowMap.maximumDistance,
0.0,
new Cesium.Cartesian4()
);
},
shadowMap_texelSizeDepthBiasAndNormalShadingSmooth: () => {
this.shadowMap.update(Reflect.get(this.viewer.scene, "_frameState"));
const bias = this.shadowMap._pointBias;
const scratchTexelStepSize = new Cesium.Cartesian2();
const texelStepSize = scratchTexelStepSize;
texelStepSize.x = 1.0 / this.shadowMap._textureSize.x;
texelStepSize.y = 1.0 / this.shadowMap._textureSize.y;
return Cesium.Cartesian4.fromElements(
texelStepSize.x,
texelStepSize.y,
bias.depthBias,
bias.normalShadingSmooth,
new Cesium.Cartesian4()
);
},
camera_projection_matrix: this.lightCamera.frustum.projectionMatrix,
camera_view_matrix: this.lightCamera.viewMatrix,
helsing_visibleAreaColor: Cesium.Color.fromCssColorString(this.visibleAreaColor),
helsing_invisibleAreaColor: Cesium.Color.fromCssColorString(this.invisibleAreaColor),
}
});
this.postStage = this.viewer.scene.postProcessStages.add(postStage);
}
drawFrustumOutline() {
const scratchRight = new Cesium.Cartesian3();
const scratchRotation = new Cesium.Matrix3();
const scratchOrientation = new Cesium.Quaternion();
const position = this.lightCamera.positionWC;
const direction = this.lightCamera.directionWC;
const up = this.lightCamera.upWC;
let right = this.lightCamera.rightWC;
right = Cesium.Cartesian3.negate(right, scratchRight);
let rotation = scratchRotation;
Cesium.Matrix3.setColumn(rotation, 0, right, rotation);
Cesium.Matrix3.setColumn(rotation, 1, up, rotation);
Cesium.Matrix3.setColumn(rotation, 2, direction, rotation);
let orientation = Cesium.Quaternion.fromRotationMatrix(rotation, scratchOrientation);
let instance = new Cesium.GeometryInstance({
geometry: new Cesium.FrustumOutlineGeometry({
frustum: this.lightCamera.frustum,
origin: Cesium.Cartesian3.fromDegrees(this.options.viewPosition.lng, this.options.viewPosition.lat, this.options.viewPosition.alt),
orientation: orientation
}),
id: Math.random().toString(36).substr(2),
attributes: {
color: Cesium.ColorGeometryInstanceAttribute.fromColor(
Cesium.Color.YELLOWGREEN//new Cesium.Color(0.0, 1.0, 0.0, 1.0)
),
show: new Cesium.ShowGeometryInstanceAttribute(true)
}
});
console.log('this.lightCamera.frustum', this.lightCamera.frustum)
let frustum = this.lightCamera.frustum.clone()
// frustum.near = frustum.far-1
let instance2 = new Cesium.GeometryInstance({
geometry: new Cesium.FrustumGeometry({
frustum: frustum,
origin: Cesium.Cartesian3.fromDegrees(this.options.viewPosition.lng, this.options.viewPosition.lat, this.options.viewPosition.alt),
orientation: orientation,
vertexFormat: Cesium.VertexFormat.POSITION_ONLY,
}),
id: Math.random().toString(36).substr(2),
attributes: {
color: Cesium.ColorGeometryInstanceAttribute.fromColor(
Cesium.Color.YELLOWGREEN.withAlpha(0.1)//new Cesium.Color(0.0, 1.0, 0.0, 1.0)
),
show: new Cesium.ShowGeometryInstanceAttribute(true)
}
});
this.frustumOutline = this.viewer.scene.primitives.add(
new Cesium.Primitive({
geometryInstances: [instance],
appearance: new Cesium.PerInstanceColorAppearance({
flat: true,
translucent: false
})
})
);
this.FrustumBottomSurface = this.viewer.scene.primitives.add(
new Cesium.Primitive({
geometryInstances: [instance2],
appearance: new Cesium.PerInstanceColorAppearance({
flat: false,
translucent: true
})
})
);
const radius = this.viewDistance;
const angle = this.viewHeading;
let Ea = 6378137; // 赤道半径
let Eb = 6356725; // 极半径
const lng = this.options.viewPosition.lng,
lat = this.options.viewPosition.lat;
let positionArr = [];
let boundaryPoints = [];
positionArr.push(lng, lat);
boundaryPoints.push([lng, lat]);
//正北是0°
let start = angle + 45 > 360 ? angle - 45 - 360 : angle - 45;
let end = start + 90;
for (let i = start; i <= end; i++) {
let dx = radius * Math.sin((i * Math.PI) / 180.0);
let dy = radius * Math.cos((i * Math.PI) / 180.0);
let ec = Eb + ((Ea - Eb) * (90.0 - lat)) / 90.0;
let ed = ec * Math.cos((lat * Math.PI) / 180);
let BJD = lng + ((dx / ed) * 180.0) / Math.PI;
let BWD = lat + ((dy / ec) * 180.0) / Math.PI;
positionArr.push(BJD, BWD);
}
console.log('positionArr', positionArr)
// let polygon = new Cesium.PolygonGeometry({
// polygonHierarchy: new Cesium.PolygonHierarchy(
// Cesium.Cartesian3.fromDegreesArray(positionArr)
// ),
// height: 0.0,
// extrudedHeight: 0.0,
// vertexFormat: Cesium.PerInstanceColorAppearance.VERTEX_FORMAT,
// stRotation: 0.0, // 纹理的旋转坐标(以弧度为单位),正旋转是逆时针方向
// ellipsoid: Cesium.Ellipsoid.WGS84,
// granularity: Cesium.Math.RADIANS_PER_DEGREE, // 每个纬度和经度之间的距离(以弧度为单位),确定缓冲区中的位置数
// perPositionHeight: false, // 每个位置点使用的高度
// closeTop: true,
// closeBottom: true,
// // NONE 与椭圆表面不符的直线;GEODESIC 遵循测地路径;RHUMB 遵循大黄蜂或恶魔般的道路。
// arcType: Cesium.ArcType.GEODESIC, // 多边形边缘线型
// });
// let polygonInstance = new Cesium.GeometryInstance({
// geometry: polygon,
// name: "ViewershedPolygon",
// attributes: {
// color: Cesium.ColorGeometryInstanceAttribute.fromColor(
// Cesium.Color.BLUE.withAlpha(0.6)
// ),
// show: new Cesium.ShowGeometryInstanceAttribute(true), //显示或者隐藏
// },
// });
// this.FrustumBottomSurface = this.viewer.scene.primitives.add(
// new Cesium.GroundPrimitive({
// geometryInstances: polygonInstance,
// appearance: new Cesium.PerInstanceColorAppearance({
// flat: false,
// translucent: false
// })
// })
// );
// this.FrustumBottomSurface.shadows = 1
// console.log(Cesium.GeometryPipeline.toWireframe(polygon))
// var model = this.viewer.scene.primitives.add(new Cesium.Model({
// // 通过GeometryPipeline.toWireframeGeometry将多边形转换为线框模型
// geometry: Cesium.GeometryPipeline.toWireframe(polygon),
// // 设置一个简单的材质
// material: Cesium.Material.fromType('Color')
// }));
}
drawSketch() {
this.sketch = this.viewer.entities.add({
name: 'sketch',
position: Cesium.Cartesian3.fromDegrees(this.options.viewPosition.lng, this.options.viewPosition.lat, this.options.viewPosition.alt),
orientation: Cesium.Transforms.headingPitchRollQuaternion(
Cesium.Cartesian3.fromDegrees(this.options.viewPosition.lng, this.options.viewPosition.lat, this.options.viewPosition.alt),
Cesium.HeadingPitchRoll.fromDegrees(this.viewHeading - 90, this.viewPitch, 0.0)
),
ellipsoid: {
radii: new Cesium.Cartesian3(
this.viewDistance,
this.viewDistance,
this.viewDistance
),
// innerRadii: new Cesium.Cartesian3(2.0, 2.0, 2.0),
minimumClock: Cesium.Math.toRadians(-this.horizontalViewAngle / 2),
maximumClock: Cesium.Math.toRadians(this.horizontalViewAngle / 2),
minimumCone: Cesium.Math.toRadians(this.verticalViewAngle + 7.75),
maximumCone: Cesium.Math.toRadians(180 - this.verticalViewAngle - 7.75),
fill: false,
outline: true,
subdivisions: 256,
stackPartitions: 64,
slicePartitions: 64,
outlineColor: Cesium.Color.YELLOWGREEN
}
});
}
}
function getHeading(fromPosition, toPosition) {
let finalPosition = new Cesium.Cartesian3();
let matrix4 = Cesium.Transforms.eastNorthUpToFixedFrame(fromPosition);
Cesium.Matrix4.inverse(matrix4, matrix4);
Cesium.Matrix4.multiplyByPoint(matrix4, toPosition, finalPosition);
Cesium.Cartesian3.normalize(finalPosition, finalPosition);
return Cesium.Math.toDegrees(Math.atan2(finalPosition.x, finalPosition.y));
}
function getPitch(fromPosition, toPosition) {
let finalPosition = new Cesium.Cartesian3();
let matrix4 = Cesium.Transforms.eastNorthUpToFixedFrame(fromPosition);
Cesium.Matrix4.inverse(matrix4, matrix4);
Cesium.Matrix4.multiplyByPoint(matrix4, toPosition, finalPosition);
Cesium.Cartesian3.normalize(finalPosition, finalPosition);
return Cesium.Math.toDegrees(Math.asin(finalPosition.z));
}
export default Test;

View File

@ -0,0 +1,117 @@
/**
* @description 测试
*/
import MaterialProperty from "../../Materail/MaterialProperty";
class TestMaterialProperty extends MaterialProperty {
constructor(options = {}) {
super(options);
/**
* 定义Cesium材质对象
*/
Cesium.Material.TestMaterialPropertyType = "TestMaterialProperty";
Cesium.Material._materialCache.addMaterial(
Cesium.Material.TestMaterialPropertyType,
{
fabric: {
type: Cesium.Material.TestMaterialPropertyType,
uniforms: {
color: new Cesium.Color(1.0, 0.0, 0.0, 0.1),
image: Cesium.Material.DefaultImageId,
tmask: Cesium.Material.DefaultImageId,
speed: 1,
repeat: new Cesium.Cartesian2(1, 1),
rotate: 0
},
source: `uniform sampler2D image;
uniform float speed;
uniform vec4 color;
uniform vec2 repeat;
czm_material czm_getMaterial(czm_materialInput materialInput){
czm_material material=czm_getDefaultMaterial(materialInput);
mat2 rotationMatrix = mat2(cos(radians(-rotate)), sin(radians(-rotate)), -sin(radians(-rotate)), cos(radians(-rotate)));
vec2 st=repeat*materialInput.st*rotationMatrix;
float time=fract(czm_frameNumber);
vec4 colorImage = texture2D(image,vec2(fract(st.s-time),st.t));
vec4 maskImage = texture2D(tmask, vec2(st.s, st.t));
material.alpha=colorImage.a*maskImage.r;
material.diffuse=colorImage.rgb;
return material;
}`,
},
isTranslucent: function () {
return true;
},
}
);
// Object.defineProperties(PolylineImageTrailMaterialProperty.prototype, {
// color: Cesium.createPropertyDescriptor("color"),
// speed: Cesium.createPropertyDescriptor("speed"),
// image: Cesium.createPropertyDescriptor("image"),
// repeat: Cesium.createPropertyDescriptor("repeat"),
// });
this._image = undefined;
this._tmask = undefined;
this._imageSubscription = undefined;
this._repeat = undefined;
this._repeatSubscription = undefined;
this.image = options.image;
this.tmask = options.tmask;
this.repeat = new Cesium.Cartesian2(
options.repeat?.x || 1,
options.repeat?.y || 1
);
this.rotate = options.rotate
let i=1
// setInterval(() => {
// this.repeat = new Cesium.Cartesian2(
// i++,
// options.repeat?.y || 1
// );
// console.log(this.repeat)
// }, 1000);
// setInterval(() => {
// this.rotate ++
// }, 100);
}
getType() {
return Cesium.Material.TestMaterialPropertyType;
}
getValue(time, result) {
if (!result) {
result = {};
}
result.color = this.color;
result.image = this.image;
result.tmask = this.tmask;
result.repeat = this.repeat;
result.speed = this.speed;
result.rotate = this.rotate
// console.log(result.repeat)
return result;
}
equals(other) {
return (
this === other ||
(other instanceof TestMaterialProperty &&
Cesium.Property.equals(this.color, other._color) &&
Cesium.Property.equals(this.image, other._image) &&
Cesium.Property.equals(this.tmask, other._tmask) &&
Cesium.Property.equals(this.repeat, other._repeat) &&
Cesium.Property.equals(this.speed, other._speed) &&
Cesium.Property.equals(this.rotate, other._rotate))
);
}
}
export default TestMaterialProperty;

View File

@ -0,0 +1,78 @@
import { attributeElm, labelStyleElm1, labelStyleElm2 } from '../../Element/elm_html'
function html(that) {
return `
<span class="custom-divider"></span>
<div class="div-item">
<div class="row" style="align-items: flex-start;">
<div class="col">
<span class="label">名称</span>
<input class="input" maxlength="40" type="text" @model="name">
</div>
<div class="col" style="flex: 0 0 60%;">
<div class="row">
<div class="col input-select-unit-box">
<span class="label" style="margin-right: 0px;">占地面积:</span>
<input class="input input-text" readonly="readonly" type="text" @model="area">
<div class="input-select-unit"></div>
</div>
</div>
</div>
</div>
</div>
<div class="div-item">
<div class="row">
<DIV-cy-tabs id="polygon-object-edit-tabs">
<DIV-cy-tab-pane label="属性信息">
${attributeElm(that)}
</DIV-cy-tab-pane>
<DIV-cy-tab-pane label="面风格">
<div class="row">
<div class="col">
<span class="label">面颜色</span>
<div class="color"></div>
</div>
<div class="col">
<span class="label">描边颜色</span>
<div class="lineColor"></div>
</div>
<div class="col">
<span class="label">描边宽度</span>
<div class="input-number input-number-unit-2">
<input class="input" type="number" title="" min="0" max="99" @model="lineWidth">
<span class="unit">px</span>
<span class="arrow"></span>
</div>
</div>
</div>
<div class="row">
<div class="col">
<span class="label">面贴地</span>
<input class="btn-switch" type="checkbox" @model="ground">
</div>
<div class="col" style="flex: 0 0 165px;">
<span class="label">面高度</span>
<div class="input-number input-number-unit-2">
<input class="input height" type="number" title="" min="-999999" max="9999999" style="width: 120px;">
<span class="unit">m</span>
<span class="arrow"></span>
</div>
</div>
<div class="col">
</div>
</div>
</DIV-cy-tab-pane>
<DIV-cy-tab-pane label="标注风格">
${labelStyleElm1()}
</DIV-cy-tab-pane>
<DIV-cy-tab-pane label="标签风格">
${labelStyleElm2()}
</DIV-cy-tab-pane>
</DIV-cy-tabs>
</div>
</div>
<span class="custom-divider"></span>
`
}
export { html }

View File

@ -0,0 +1,511 @@
/*
* @Author: Wang jianLei
* @Date: 2022-05-17 21:49:28
* @Last Modified by: Wang JianLei
* @Last Modified time: 2022-05-19 22:08:14
*/
let ViewShed = function (sdk, canvasEleId) {
if (!sdk.viewer) throw new Error("no viewer object!");
let canvasEle = document.getElementById(canvasEleId);
if (!canvasEle) throw new Error("the canvas element is not exist");
this.canvasEle = canvasEle;
this.viewer = sdk.viewer;
this.handler = undefined;
this.lightCamera;
this.pyramid;
this.frustumPrimitive;
this.viewershedPolygon;
};
ViewShed.prototype = {
/**
* 初始化handler
*/
initHandler() {
if (this.handler) {
this.handler.destroy();
this.handler = undefined;
}
},
/**
* 开始执行视域分析
* @param {number} precision 精度值越大创建耗时越长建议在10~20之间
*/
createViewshed: function (precision) {
let $this = this;
let scene = $this.viewer.scene;
$this.initHandler();
$this.clearAll();
$this.handler = new Cesium.ScreenSpaceEventHandler($this.viewer.canvas);
$this.handler.setInputAction((event) => {
// 禁止地球旋转和缩放,地球的旋转会对鼠标移动监听有影响,所以需要禁止
scene.screenSpaceCameraController.enableRotate = false;
scene.screenSpaceCameraController.enableZoom = false;
scene.globe.depthTestAgainstTerrain = true;
let earthPosition = scene.pickPosition(event.position);
let pos = $this.cartesian3ToDegree(earthPosition);
$this.handler.setInputAction(function (event) {
let newPosition = scene.pickPosition(event.endPosition);
if (Cesium.defined(newPosition)) {
let pos1 = $this.cartesian3ToDegree(newPosition);
let distance = Cesium.Cartesian3.distance(newPosition, earthPosition);
let angle = $this.getAngle(pos[0], pos[1], pos1[0], pos1[1]);
let pitch = $this.getPitch(earthPosition, newPosition);
$this.ViewShedOptions = {
viewPosition: earthPosition, //观测点 笛卡尔坐标
endPosition: newPosition, //目标点 笛卡尔坐标
direction: angle, //观测方位角 默认为`0`,范围`0~360`
pitch: pitch, //俯仰角,radius,默认为`0`
horizontalViewAngle: 90, //可视域水平夹角,默认为 `90`,范围`0~360`
verticalViewAngle: 60, //可视域垂直夹角,默认为`60`,范围`0~180`
visibleAreaColor: Cesium.Color.GREEN, //可视区域颜色,默认为`green`
invisibleAreaColor: Cesium.Color.RED, //不可见区域颜色,默认为`red`
visualRange: distance, //距离,单位`米`
};
$this.updateViewShed();
}
}, Cesium.ScreenSpaceEventType.MOUSE_MOVE);
}, Cesium.ScreenSpaceEventType.LEFT_DOWN);
$this.handler.setInputAction(() => {
$this.initHandler();
// 开启地球旋转和缩放
scene.screenSpaceCameraController.enableRotate = true;
scene.screenSpaceCameraController.enableZoom = true;
$this.drawViewershed(precision);
}, Cesium.ScreenSpaceEventType.LEFT_UP);
},
ReturnDistance(pos0, pos1) {
let distance = 0;
let point1cartographic = Cesium.Cartographic.fromCartesian(pos0);
let point2cartographic = Cesium.Cartographic.fromCartesian(pos1);
/**根据经纬度计算出距离**/
let geodesic = new Cesium.EllipsoidGeodesic();
geodesic.setEndPoints(point1cartographic, point2cartographic);
let s = geodesic.surfaceDistance;
return s;
},
getHeight(x, y, objectsToExclude) {
let endCartographic = Cesium.Cartographic.fromDegrees(x, y);
let endHeight = this.viewer.scene.sampleHeight(
endCartographic,
objectsToExclude
);
return endHeight;
},
cartesian3ToDegree: function (Cartesian3) {
let _ellipsoid = this.viewer.scene.globe.ellipsoid;
let _cartographic = _ellipsoid.cartesianToCartographic(Cartesian3);
let _lat = Cesium.Math.toDegrees(_cartographic.latitude);
let _lng = Cesium.Math.toDegrees(_cartographic.longitude);
let _alt = _cartographic.height;
return [_lng, _lat, _alt];
},
getAngle: function (lng1, lat1, lng2, lat2) {
let dRotateAngle = Math.atan2(Math.abs(lng1 - lng2), Math.abs(lat1 - lat2));
if (lng2 >= lng1) {
dRotateAngle = lat2 < lat1 ? Math.PI - dRotateAngle : dRotateAngle;
} else {
dRotateAngle =
lat2 >= lat1 ? 2 * Math.PI - dRotateAngle : Math.PI + dRotateAngle;
}
dRotateAngle = (dRotateAngle * 180) / Math.PI;
return dRotateAngle;
},
getPitch(pointA, pointB) {
let transfrom = Cesium.Transforms.eastNorthUpToFixedFrame(pointA);
const vector = Cesium.Cartesian3.subtract(
pointB,
pointA,
new Cesium.Cartesian3()
);
let direction = Cesium.Matrix4.multiplyByPointAsVector(
Cesium.Matrix4.inverse(transfrom, transfrom),
vector,
vector
);
Cesium.Cartesian3.normalize(direction, direction);
return Cesium.Math.PI_OVER_TWO - Cesium.Math.acosClamped(direction.z);
},
updateViewShed: function () {
this.clear();
this.setLightCamera();
this.addVisualPyramid();
this.createFrustum();
},
clear: function () {
if (this.pyramid) {
this.viewer.entities.removeById(this.pyramid.id);
this.pyramid = undefined;
}
if (this.frustumPrimitive) {
this.viewer.scene.primitives.remove(this.frustumPrimitive);
this.frustumPrimitive = undefined;
}
if (this.debugModelMatrixPrimitive) {
this.viewer.scene.primitives.remove(this.debugModelMatrixPrimitive);
this.debugModelMatrixPrimitive = undefined;
}
},
clearAll: function () {
this.clear();
if (this.viewershedPolygon) {
this.viewer.scene.primitives.remove(this.viewershedPolygon);
this.viewershedPolygon = undefined;
}
},
addVisualPyramid: function () {
let options = this.ViewShedOptions;
let position = options.viewPosition;
let visualRange = Number(options.visualRange);
let transform = Cesium.Transforms.eastNorthUpToFixedFrame(position);
this.debugModelMatrixPrimitive = this.viewer.scene.primitives.add(
new Cesium.DebugModelMatrixPrimitive({
modelMatrix: transform,
length: 5.0,
})
);
const halfClock = options.horizontalViewAngle / 2;
const halfCone = options.verticalViewAngle / 2;
const pitch = Cesium.Math.toDegrees(options.pitch);
const ellipsoid = new Cesium.EllipsoidGraphics({
radii: new Cesium.Cartesian3(visualRange, visualRange, visualRange),
minimumClock: Cesium.Math.toRadians(90 - options.direction - halfClock),
maximumClock: Cesium.Math.toRadians(90 - options.direction + halfClock),
minimumCone: Cesium.Math.toRadians(90 - pitch - halfCone),
maximumCone: Cesium.Math.toRadians(90 - pitch + halfCone),
fill: false,
outline: true,
subdivisions: 256,
stackPartitions: 64,
slicePartitions: 64,
outlineColor: Cesium.Color.YELLOWGREEN.withAlpha(0.5),
});
const pyramidEntity = new Cesium.Entity({
position: position,
ellipsoid,
});
this.pyramid = this.viewer.entities.add(pyramidEntity);
},
setLightCamera: function () {
if (!this.lightCamera) {
this.lightCamera = new Cesium.Camera(this.viewer.scene);
}
let options = this.ViewShedOptions;
let visualRange = Number(options.visualRange);
this.lightCamera.position = options.viewPosition;
this.lightCamera.frustum.near = 0.1;
this.lightCamera.frustum.far = visualRange;
const hr = Cesium.Math.toRadians(options.horizontalViewAngle);
const vr = Cesium.Math.toRadians(options.verticalViewAngle);
this.lightCamera.frustum.aspectRatio =
(visualRange * Math.tan(hr / 2) * 2) /
(visualRange * Math.tan(vr / 2) * 2);
this.lightCamera.frustum.fov = hr > vr ? hr : vr;
this.lightCamera.setView({
destination: options.viewPosition,
orientation: {
heading: Cesium.Math.toRadians(options.direction || 0),
pitch: options.pitch || 0,
roll: 0,
},
});
},
createFrustum: function () {
const scratchRight = new Cesium.Cartesian3();
const scratchRotation = new Cesium.Matrix3();
const scratchOrientation = new Cesium.Quaternion();
const direction = this.lightCamera.directionWC;
const up = this.lightCamera.upWC;
let right = this.lightCamera.rightWC;
right = Cesium.Cartesian3.negate(right, scratchRight);
let rotation = scratchRotation;
Cesium.Matrix3.setColumn(rotation, 0, right, rotation);
Cesium.Matrix3.setColumn(rotation, 1, up, rotation);
Cesium.Matrix3.setColumn(rotation, 2, direction, rotation);
let orientation = Cesium.Quaternion.fromRotationMatrix(
rotation,
scratchOrientation
);
let instanceOutline = new Cesium.GeometryInstance({
geometry: new Cesium.FrustumOutlineGeometry({
frustum: this.lightCamera.frustum,
origin: this.ViewShedOptions.viewPosition,
orientation: orientation,
}),
id: "视椎体轮廓线" + Math.random().toString(36).substr(2),
attributes: {
color: Cesium.ColorGeometryInstanceAttribute.fromColor(
new Cesium.Color(0.0, 1.0, 0.0, 1.0)
),
show: new Cesium.ShowGeometryInstanceAttribute(true),
},
});
this.frustumPrimitive = this.viewer.scene.primitives.add(
new Cesium.Primitive({
geometryInstances: instanceOutline,
appearance: new Cesium.PerInstanceColorAppearance({
flat: true,
translucent: false,
closed: true,
}),
})
);
},
createPoint: function (firstPos, secondPos) {
let entity4FirstPos = new Cesium.Entity({
name: "firstPos",
show: true,
position: firstPos,
point: {
show: true,
pixelSize: 20,
color: Cesium.Color.RED,
outlineColor: Cesium.Color.YELLOW,
outlineWidth: 5,
},
description: `
<p>这是绘制的视椎体起点</p>`,
});
this.viewer.entities.add(entity4FirstPos);
let entity4SecondPos = new Cesium.Entity({
name: "secondPos",
show: true,
position: secondPos,
point: {
show: true,
pixelSize: 30,
color: Cesium.Color.YELLOW,
outlineColor: Cesium.Color.RED,
outlineWidth: 8,
},
description: `
<p>这是绘制的视椎体视角终点</p>`,
});
this.viewer.entities.add(entity4SecondPos);
},
//绘制可视域
add(positionArr) {
let polygon = new Cesium.PolygonGeometry({
polygonHierarchy: new Cesium.PolygonHierarchy(
Cesium.Cartesian3.fromDegreesArray(positionArr)
),
height: 0.0,
extrudedHeight: 0.0,
vertexFormat: Cesium.PerInstanceColorAppearance.VERTEX_FORMAT,
stRotation: 0.0, // 纹理的旋转坐标(以弧度为单位),正旋转是逆时针方向
ellipsoid: Cesium.Ellipsoid.WGS84,
granularity: Cesium.Math.RADIANS_PER_DEGREE, // 每个纬度和经度之间的距离(以弧度为单位),确定缓冲区中的位置数
perPositionHeight: false, // 每个位置点使用的高度
closeTop: true,
closeBottom: true,
// NONE 与椭圆表面不符的直线;GEODESIC 遵循测地路径;RHUMB 遵循大黄蜂或恶魔般的道路。
arcType: Cesium.ArcType.GEODESIC, // 多边形边缘线型
});
let polygonInstance = new Cesium.GeometryInstance({
geometry: polygon,
name: "ViewershedPolygon",
attributes: {
color: Cesium.ColorGeometryInstanceAttribute.fromColor(
Cesium.Color.BLUE.withAlpha(0.6)
),
show: new Cesium.ShowGeometryInstanceAttribute(true), //显示或者隐藏
},
});
this.viewershedPolygon = this.viewer.scene.primitives.add(
new Cesium.GroundPrimitive({
geometryInstances: polygonInstance,
appearance: new Cesium.EllipsoidSurfaceAppearance({
aboveGround: true,
material: new Cesium.Material({
fabric: {
type: "Image",
uniforms: {
image: this.returnImgae(),
},
},
}),
}),
})
);
},
drawViewershed(precision) {
const pos = this.cartesian3ToDegree(this.ViewShedOptions.viewPosition);
const radius = this.ViewShedOptions.visualRange;
const direction = this.ViewShedOptions.direction;
let boundary = this.computeBoundaryOptions(pos, radius, direction);
const bbox = boundary.bbox;
let mask = turf.polygon([boundary.boundaryPoints]);
const dis = this.ViewShedOptions.visualRange / (precision * 1000);
let gridPoints = turf.pointGrid(bbox, dis, { mask: mask });
let pointsResult = this.createTargetPoints(gridPoints, dis, pos);
let variogram = kriging.train(
pointsResult.values,
pointsResult.lngs,
pointsResult.lats,
"exponential",
0,
100
);
let grid = kriging.grid([boundary.boundaryPoints], variogram, dis / 1000);
const colors = [
"#ff000080",
"#ff000080",
"#ff000080",
"#ff000080",
"#ff000080",
"#ff000080",
"#00ff0080",
"#00ff0080",
"#00ff0080",
"#00ff0080",
"#00ff0080",
"#00ff0080",
];
this.canvasEle.width = 3840;
this.canvasEle.height = 2160;
kriging.plot(
this.canvasEle,
grid,
[bbox[0], bbox[2]],
[bbox[1], bbox[3]],
colors
);
this.add(boundary.positionArr);
},
computeBoundaryOptions(pos, radius, angle) {
let Ea = 6378137; // 赤道半径
let Eb = 6356725; // 极半径
const lng = pos[0],
lat = pos[1];
const bbox = [lng, lat, lng, lat]; //[minX, minY, maxX, maxY]
let positionArr = [];
let boundaryPoints = [];
positionArr.push(lng, lat);
boundaryPoints.push([lng, lat]);
//正北是0°
let start = angle + 45 > 360 ? angle - 45 - 360 : angle - 45;
let end = start + 90;
for (let i = start; i <= end; i++) {
let dx = radius * Math.sin((i * Math.PI) / 180.0);
let dy = radius * Math.cos((i * Math.PI) / 180.0);
let ec = Eb + ((Ea - Eb) * (90.0 - lat)) / 90.0;
let ed = ec * Math.cos((lat * Math.PI) / 180);
let BJD = lng + ((dx / ed) * 180.0) / Math.PI;
let BWD = lat + ((dy / ec) * 180.0) / Math.PI;
positionArr.push(BJD, BWD);
boundaryPoints.push([BJD, BWD]);
this.refreshBBox(bbox, BJD, BWD);
}
boundaryPoints.push([lng, lat]);
return {
positionArr,
boundaryPoints,
bbox,
};
},
/**
* 更新外围矩形 Bbox
* @param {Array} result 外围矩形Bbox-[minX, minY, maxX, maxY]
* @param {Number} x 经度
* @param {Number} y 纬度
*/
refreshBBox(result, x, y) {
result[0] = x < result[0] ? x : result[0];
result[1] = y < result[1] ? y : result[1];
result[2] = x > result[2] ? x : result[2];
result[3] = y > result[3] ? y : result[3];
},
/**
* 插值点用射线判断通视性
* @param {*} gridPoints 网格点
* @param {*} step 步长,可以理解成是精度
* @param {*} sourcePos 视域分析起点
* @returns kriging插值所需的参数对象{ values:[], lngs:[], lats:[]}
*/
createTargetPoints(gridPoints, step, sourcePos) {
let positionArr = [];
let objectsToExclude = [
this.frustumPrimitive,
this.pyramid,
this.debugModelMatrixPrimitive,
];
let values = [],
lngs = [],
lats = [];
let height = this.getHeight(sourcePos[0], sourcePos[1], objectsToExclude);
positionArr.push({
x: sourcePos[0],
y: sourcePos[1],
z: height,
});
let viewPoint = this.ViewShedOptions.viewPosition;
for (let index = 0; index < gridPoints.features.length; index++) {
const feature = gridPoints.features[index];
const coords = feature.geometry.coordinates;
const x = coords[0],
y = coords[1];
let h = this.getHeight(x, y, objectsToExclude);
let endPoint = Cesium.Cartesian3.fromDegrees(x, y, h);
let direction = Cesium.Cartesian3.normalize(
Cesium.Cartesian3.subtract(
endPoint,
viewPoint,
new Cesium.Cartesian3()
),
new Cesium.Cartesian3()
);
// 建立射线
let ray = new Cesium.Ray(viewPoint, direction);
let result = this.viewer.scene.pickFromRay(ray, objectsToExclude); // 计算交互点,返回第一个
if (result) {
let buffer = this.ReturnDistance(endPoint, result.position);
// let M_color = Cesium.Color.GREEN;
if (buffer > step) {
// M_color = Cesium.Color.RED;
values.push(0);
} else {
values.push(1);
}
lngs.push(x);
lats.push(y);
// this.viewer.entities.add(
// new Cesium.Entity({
// name: "插值点哦",
// show: true,
// position: endPoint,
// point: {
// show: true,
// pixelSize: 10,
// color: M_color,
// outlineWidth: 2,
// outlineColor: Cesium.Color.YELLOW,
// },
// })
// );
}
}
return {
values,
lngs,
lats,
};
},
/**
* canvas转image图片
* @returns base64图片
*/
returnImgae() {
return this.canvasEle.toDataURL("image/png");
},
};
export default ViewShed;

View File

@ -0,0 +1,72 @@
export default `
#define USE_CUBE_MAP_SHADOW true
uniform sampler2D colorTexture;
uniform sampler2D depthTexture;
varying vec2 v_textureCoordinates;
uniform mat4 camera_projection_matrix;
uniform mat4 camera_view_matrix;
uniform vec4 helsing_visibleAreaColor;
uniform vec4 helsing_invisibleAreaColor;
uniform sampler2D helsing_texture;
vec4 getPositionEC(){
return czm_windowToEyeCoordinates(gl_FragCoord);
}
vec3 getNormalEC(){
return vec3(1.);
}
vec4 toEye(in vec2 uv,in float depth){
vec2 xy=vec2((uv.x*2.-1.),(uv.y*2.-1.));
vec4 posInCamera=czm_inverseProjection*vec4(xy,depth,1.);
posInCamera=posInCamera/posInCamera.w;
return posInCamera;
}
vec3 pointProjectOnPlane(in vec3 planeNormal,in vec3 planeOrigin,in vec3 point){
vec3 v01=point-planeOrigin;
float d=dot(planeNormal,v01);
return(point-planeNormal*d);
}
float getDepth(in vec4 depth){
float z_window=czm_unpackDepth(depth);
z_window=czm_reverseLogDepth(z_window);
float n_range=czm_depthRange.near;
float f_range=czm_depthRange.far;
return(2.*z_window-n_range-f_range)/(f_range-n_range);
}
bool visible(in vec4 result)
{
result.x/=result.w;
result.y/=result.w;
result.z/=result.w;
return result.x>=-1.&&result.x<=1.
&&result.y>=-1.&&result.y<=1.
&&result.z>=-1.&&result.z<=1.;
}
void main(){
// 釉色 = 结构二维(颜色纹理, 纹理坐标)
gl_FragColor = texture2D(colorTexture, v_textureCoordinates);
// 深度 = 获取深度(结构二维(深度纹理, 纹理坐标))
float depth = getDepth(texture2D(depthTexture, v_textureCoordinates));
// 视角 = (纹理坐标, 深度)
vec4 positionEC = toEye(v_textureCoordinates, depth);
// 世界坐标
vec4 wordPos = czm_inverseView * positionEC;
// 虚拟相机中坐标
vec4 vcPos = camera_view_matrix * wordPos;
vec4 videoColor = texture2D(helsing_texture, v_textureCoordinates);
float dis = length(vcPos.xyz);
vec4 posInEye = camera_projection_matrix * vcPos;
// 可视区颜色
// vec4 helsing_visibleAreaColor=vec4(0.,1.,0.,.5);
// vec4 helsing_invisibleAreaColor=vec4(1.,0.,0.,.5);
if(visible(posInEye)){
vec4 out_FragColor = helsing_visibleAreaColor;
gl_FragColor = mix(gl_FragColor,videoColor,1.0);
}
}`;

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,184 @@
import Base from "../../Base/index";
class PolygonObject extends Base {
constructor(sdk, options = {}, _Dialog = {}) {
super(sdk, options);
this.create()
}
create() {
let fragmentShaderSource = `
varying vec3 v_normal;
varying vec2 v_uv;
uniform vec2 repeat;
uniform sampler2D image;
uniform vec4 color;
uniform sampler2D colorTexture;
czm_material czm_getMaterial(czm_materialInput materialInput)
{
czm_material material = czm_getDefaultMaterial(materialInput);
material.diffuse = czm_gammaCorrect(texture2D(image, fract(repeat * materialInput.st)).rgb * color.rgb);
material.alpha = texture2D(image, fract(repeat * materialInput.st)).a * color.a;
return material;
}
varying vec3 v_positionMC;
varying vec3 v_positionEC;
varying vec2 v_st;
void main()
{
czm_materialInput materialInput;
vec3 normalEC = normalize(czm_normal3D * czm_geodeticSurfaceNormal(v_positionMC, vec3(0.0), vec3(1.0)));
#ifdef FACE_FORWARD
normalEC = faceforward(normalEC, vec3(0.0, 0.0, 1.0), -normalEC);
#endif
materialInput.s = v_st.s;
materialInput.st = v_st;
materialInput.str = vec3(v_st, 0.0);
materialInput.normalEC = normalEC;
materialInput.tangentToEyeMatrix = czm_eastNorthUpToEyeCoordinates(v_positionMC, materialInput.normalEC);
vec3 positionToEyeEC = -v_positionEC;
materialInput.positionToEyeEC = positionToEyeEC;
czm_material material = czm_getMaterial(materialInput);
#ifdef FLAT
gl_FragColor = vec4(material.diffuse + material.emission, material.alpha);
#else
gl_FragColor = czm_phong(normalize(positionToEyeEC), material, czm_lightDirectionEC);
#endif
}
`
const vertexShaderSource = `
// attribute vec3 position;
// attribute vec3 normal;
// varying vec3 v_normal;
// attribute vec2 uv;
// varying vec2 v_uv;
// void main()
// {
// gl_Position = czm_modelViewProjection * vec4(position, 1.);
// v_normal = normal;
// v_uv = uv;
// }
attribute vec3 position3DHigh;
attribute vec3 position3DLow;
attribute vec2 st;
attribute float batchId;
varying vec3 v_positionMC;
varying vec3 v_positionEC;
varying vec2 v_st;
attribute vec3 position;
void main()
{
vec4 p = vec4(position, 1.);
v_positionMC = position3DHigh + position3DLow;
v_positionEC = (czm_modelViewRelativeToEye * p).xyz;
v_st = st;
gl_Position = czm_modelViewProjectionRelativeToEye * p;
}
`
class ly_primitive {
constructor(options) {
this.drawCommand = undefined
if (Cesium.defined(options)) {
this.modelMatrix = options.modelMatrix
this.geometry = options.geometry
console.log('this.geometry', this.geometry)
}
}
createCommand(context) {
let t = performance.now()
if (!Cesium.defined(this.geometry)) return
const geometry = Cesium.BoxGeometry.createGeometry(this.geometry)
this.vertexarray = Cesium.VertexArray.fromGeometry({
context: context,
geometry: geometry
})
const renderstate = Cesium.RenderState.fromCache({
depthTest: {
enabled: true
}
// blending: {
// enabled: true
// }
// cull: {
// enabled: false,
// }
})
const shaderProgram = Cesium.ShaderProgram.fromCache({
context: context,
vertexShaderSource: vertexShaderSource,
fragmentShaderSource: fragmentShaderSource
})
const that = this
const videoElm = document.getElementsByTagName('video')[0];
let texture = new Cesium.Texture({
context: context,
source: videoElm
})
const uniformmap = {
colorTexture: function() {
texture.copyFrom({
source: videoElm
})
return texture
},
iTime: function() {
return (performance.now() - t) / 1000
}
}
this.drawCommand = new Cesium.DrawCommand({
boundingVolume: this.geometry.boundingSphere,
modelMatrix: this.modelMatrix,
// pass: Cesium.Pass.OPAQUE,
pass: Cesium.Pass.TRANSLUCENT,
shaderProgram: shaderProgram,
renderState: renderstate,
vertexArray: this.vertexarray,
uniformMap: uniformmap
})
}
update(frameState) {
if (!this.drawCommand) {
this.createCommand(frameState.context)
}
frameState.commandList.push(this.drawCommand)
}
}
let positions = this.options.positions
let fromDegreesArray = []
for (let i = 0; i < positions.length; i++) {
fromDegreesArray.push(positions[i].lng, positions[i].lat)
}
const video = document.getElementsByTagName('video')[0];
let polygon = new Cesium.PolygonGeometry({
polygonHierarchy: new Cesium.PolygonHierarchy(
Cesium.Cartesian3.fromDegreesArray(fromDegreesArray)
),
height: 100
});
const options = {
modelMatrix: Cesium.Matrix4.IDENTITY,
geometry: Cesium.PolygonGeometry.createGeometry(polygon)
}
this.sdk.viewer.scene.primitives.add(
new ly_primitive(options)
)
}
}
export default PolygonObject