This commit is contained in:
zh
2025-09-01 16:17:11 +08:00
parent d802602200
commit 6fa99df21c
1035 changed files with 377083 additions and 1 deletions

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.AnalysesResults.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;