init
This commit is contained in:
25
src/Obj/Analysis/CircleViewShed/_element.js
Normal file
25
src/Obj/Analysis/CircleViewShed/_element.js
Normal 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 }
|
||||
512
src/Obj/Analysis/CircleViewShed/_index.js
Normal file
512
src/Obj/Analysis/CircleViewShed/_index.js
Normal 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;
|
||||
131
src/Obj/Analysis/CircleViewShed/glsl.js
Normal file
131
src/Obj/Analysis/CircleViewShed/glsl.js
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}`;
|
||||
380
src/Obj/Analysis/CircleViewShed/index.js
Normal file
380
src/Obj/Analysis/CircleViewShed/index.js
Normal 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.AnalysesResults.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
|
||||
213
src/Obj/Analysis/Contour/index.js
Normal file
213
src/Obj/Analysis/Contour/index.js
Normal file
@ -0,0 +1,213 @@
|
||||
|
||||
|
||||
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.AnalysesResults.push(this)
|
||||
this.createNewLine();
|
||||
}
|
||||
|
||||
get type() {
|
||||
return 'ContourAnalysis'
|
||||
}
|
||||
|
||||
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;
|
||||
125
src/Obj/Analysis/CutFill/CreatePolygon.js
Normal file
125
src/Obj/Analysis/CutFill/CreatePolygon.js
Normal 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;
|
||||
75
src/Obj/Analysis/CutFill/_element.js
Normal file
75
src/Obj/Analysis/CutFill/_element.js
Normal 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 }
|
||||
354
src/Obj/Analysis/CutFill/index.js
Normal file
354
src/Obj/Analysis/CutFill/index.js
Normal 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.AnalysesResults.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;
|
||||
483
src/Obj/Analysis/Flat/index.js
Normal file
483
src/Obj/Analysis/Flat/index.js
Normal 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;
|
||||
227
src/Obj/Analysis/Flat/index1.js
Normal file
227
src/Obj/Analysis/Flat/index1.js
Normal 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;
|
||||
8
src/Obj/Analysis/Profile/_element.js
Normal file
8
src/Obj/Analysis/Profile/_element.js
Normal file
@ -0,0 +1,8 @@
|
||||
function html() {
|
||||
return `
|
||||
<span class="custom-divider"></span>
|
||||
<div class="profile-echarts"></div>
|
||||
`
|
||||
}
|
||||
|
||||
export { html }
|
||||
637
src/Obj/Analysis/Profile/index.js
Normal file
637
src/Obj/Analysis/Profile/index.js
Normal 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.AnalysesResults.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;
|
||||
145
src/Obj/Analysis/Section/index.js
Normal file
145
src/Obj/Analysis/Section/index.js
Normal 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.SectionResults.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;
|
||||
517
src/Obj/Analysis/SlopeAspect/index.js
Normal file
517
src/Obj/Analysis/SlopeAspect/index.js
Normal 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.AnalysesResults.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;
|
||||
92
src/Obj/Analysis/Submerge/_element.js
Normal file
92
src/Obj/Analysis/Submerge/_element.js
Normal 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 }
|
||||
484
src/Obj/Analysis/Submerge/index.js
Normal file
484
src/Obj/Analysis/Submerge/index.js
Normal 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.AnalysesResults.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
|
||||
32
src/Obj/Analysis/TerrainExcavation/_element.js
Normal file
32
src/Obj/Analysis/TerrainExcavation/_element.js
Normal 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 }
|
||||
459
src/Obj/Analysis/TerrainExcavation/index.js
Normal file
459
src/Obj/Analysis/TerrainExcavation/index.js
Normal 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;
|
||||
54
src/Obj/Analysis/ViewShed/_element.js
Normal file
54
src/Obj/Analysis/ViewShed/_element.js
Normal 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 }
|
||||
511
src/Obj/Analysis/ViewShed/_index.js
Normal file
511
src/Obj/Analysis/ViewShed/_index.js
Normal 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;
|
||||
131
src/Obj/Analysis/ViewShed/glsl.js
Normal file
131
src/Obj/Analysis/ViewShed/glsl.js
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}`;
|
||||
131
src/Obj/Analysis/ViewShed/glsl2.js
Normal file
131
src/Obj/Analysis/ViewShed/glsl2.js
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}`;
|
||||
773
src/Obj/Analysis/ViewShed/index.js
Normal file
773
src/Obj/Analysis/ViewShed/index.js
Normal 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.AnalysesResults.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;
|
||||
74
src/Obj/Analysis/ViewShed2/_element.js
Normal file
74
src/Obj/Analysis/ViewShed2/_element.js
Normal 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 }
|
||||
512
src/Obj/Analysis/ViewShed2/_index.js
Normal file
512
src/Obj/Analysis/ViewShed2/_index.js
Normal 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;
|
||||
131
src/Obj/Analysis/ViewShed2/glsl.js
Normal file
131
src/Obj/Analysis/ViewShed2/glsl.js
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}`;
|
||||
721
src/Obj/Analysis/ViewShed2/index.js
Normal file
721
src/Obj/Analysis/ViewShed2/index.js
Normal 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.AnalysesResults.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;
|
||||
74
src/Obj/Analysis/ViewShed3/_element.js
Normal file
74
src/Obj/Analysis/ViewShed3/_element.js
Normal 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 }
|
||||
512
src/Obj/Analysis/ViewShed3/_index.js
Normal file
512
src/Obj/Analysis/ViewShed3/_index.js
Normal 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;
|
||||
131
src/Obj/Analysis/ViewShed3/glsl.js
Normal file
131
src/Obj/Analysis/ViewShed3/glsl.js
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}`;
|
||||
302
src/Obj/Analysis/ViewShed3/index.js
Normal file
302
src/Obj/Analysis/ViewShed3/index.js
Normal 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;
|
||||
19
src/Obj/Analysis/Visibility/_element.js
Normal file
19
src/Obj/Analysis/Visibility/_element.js
Normal 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 }
|
||||
359
src/Obj/Analysis/Visibility/index.js
Normal file
359
src/Obj/Analysis/Visibility/index.js
Normal file
@ -0,0 +1,359 @@
|
||||
/*
|
||||
*通视分析
|
||||
* @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.AnalysesResults.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
|
||||
if(that.sdk.viewer.terrainProvider.availability)
|
||||
{
|
||||
positions = await Cesium.sampleTerrainMostDetailed(
|
||||
that.sdk.viewer.terrainProvider,
|
||||
[Cesium.Cartographic.fromDegrees(pos84.lng, pos84.lat)]
|
||||
);
|
||||
}
|
||||
if (positions && 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;
|
||||
14
src/Obj/Analysis/clear.js
Normal file
14
src/Obj/Analysis/clear.js
Normal file
@ -0,0 +1,14 @@
|
||||
function AnalysisClear() {
|
||||
YJ.Analysis.AnalysesResults.forEach(m => {
|
||||
m.destroy()
|
||||
})
|
||||
// YJ.Analysis.AnalysesResults = []
|
||||
}
|
||||
|
||||
function SectionClear() {
|
||||
YJ.Analysis.SectionResults.forEach(m => {
|
||||
m.destroy()
|
||||
})
|
||||
}
|
||||
|
||||
export {AnalysisClear, SectionClear}
|
||||
74
src/Obj/Analysis/test/_element.js
Normal file
74
src/Obj/Analysis/test/_element.js
Normal 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 }
|
||||
511
src/Obj/Analysis/test/_index.js
Normal file
511
src/Obj/Analysis/test/_index.js
Normal 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;
|
||||
72
src/Obj/Analysis/test/glsl.js
Normal file
72
src/Obj/Analysis/test/glsl.js
Normal 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);
|
||||
}
|
||||
}`;
|
||||
787
src/Obj/Analysis/test/index.js
Normal file
787
src/Obj/Analysis/test/index.js
Normal 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.AnalysesResults.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;
|
||||
117
src/Obj/Analysis/test2/TestMaterialProperty.js
Normal file
117
src/Obj/Analysis/test2/TestMaterialProperty.js
Normal 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;
|
||||
78
src/Obj/Analysis/test2/_element.js
Normal file
78
src/Obj/Analysis/test2/_element.js
Normal 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 }
|
||||
511
src/Obj/Analysis/test2/_index.js
Normal file
511
src/Obj/Analysis/test2/_index.js
Normal 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;
|
||||
72
src/Obj/Analysis/test2/glsl.js
Normal file
72
src/Obj/Analysis/test2/glsl.js
Normal 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);
|
||||
}
|
||||
}`;
|
||||
2095
src/Obj/Analysis/test2/index.js
Normal file
2095
src/Obj/Analysis/test2/index.js
Normal file
File diff suppressed because one or more lines are too long
184
src/Obj/Analysis/test2/index2.js
Normal file
184
src/Obj/Analysis/test2/index2.js
Normal 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
|
||||
Reference in New Issue
Block a user