Compare commits

..

1 Commits

Author SHA1 Message Date
BianLzhaoMin 0d2be6c56e 优化3D杆塔显示 2025-12-01 13:10:18 +08:00
4 changed files with 7796 additions and 240 deletions

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -16,6 +16,9 @@ let isMap = true; // 是否是地图模式
let projectOverview = ""; // 工程概况
let projectTitle = ""; // 项目名称
let centerTitle = ""; // 中间标题
let polylines1 = [];
let polylines2 = [];
let polylines3 = [];
// 转换函数
function dmsToDecimal(dmsString) {
@ -36,6 +39,312 @@ function dmsToDecimal(dmsString) {
return degrees + minutes / 60 + seconds / 3600;
}
/**
* 根据经纬度获取海拔高度
* @param {number} lng - 经度
* @param {number} lat - 纬度
* @returns {Promise<number>} 返回海拔高度如果获取失败则返回0
*/
async function getAltitude(lng, lat) {
try {
// 确保 map 实例已初始化
if (!map || !map.scene) {
console.warn("地图未初始化,无法获取海拔高度");
return 0;
}
const Cesium = window.mars3d.Cesium;
// 将经纬度转换为 Cartographic 对象(弧度)
const cartographic = Cesium.Cartographic.fromDegrees(lng, lat);
// 方法1: 使用 sampleTerrainMostDetailed 获取精确的地形高度(推荐)
const terrainProvider = map.scene.terrainProvider;
if (terrainProvider) {
try {
// 检查地形提供者是否支持采样
if (terrainProvider.availability || terrainProvider.ready) {
const positions = [cartographic];
const updatedPositions =
await Cesium.sampleTerrainMostDetailed(
terrainProvider,
positions
);
if (
updatedPositions &&
updatedPositions.length > 0 &&
updatedPositions[0].height !== undefined &&
updatedPositions[0].height !== null
) {
return updatedPositions[0].height;
}
}
} catch (error) {
console.warn(
"使用 sampleTerrainMostDetailed 获取海拔失败:",
error
);
}
}
// 方法2: 使用 sampleTerrain 作为备选方案(精度较低但更兼容)
if (terrainProvider && Cesium.sampleTerrain) {
try {
const level = 11; // 地形级别,可以根据需要调整
const positions = [cartographic];
const updatedPositions = await Cesium.sampleTerrain(
terrainProvider,
level,
positions
);
if (
updatedPositions &&
updatedPositions.length > 0 &&
updatedPositions[0].height !== undefined &&
updatedPositions[0].height !== null
) {
return updatedPositions[0].height;
}
} catch (error) {
console.warn("使用 sampleTerrain 获取海拔失败:", error);
}
}
// 方法3: 如果以上方法都失败返回0作为默认值
console.warn(`无法获取坐标(${lng}, ${lat})的海拔高度返回默认值0`);
return 0;
} catch (error) {
console.error("获取海拔高度时发生错误:", error);
return 0;
}
}
/**
* 方案1: 使用第三方 OpenElevation API 获取海拔推荐不依赖地形服务
* @param {number} lng - 经度
* @param {number} lat - 纬度
* @returns {Promise<number>} 返回海拔高度
*/
async function getElevationByAPI(lng, lat) {
try {
// 使用 OpenElevation API免费无需API密钥
const response = await fetch(
`https://api.open-elevation.com/api/v1/lookup?locations=${lat},${lng}`
);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
if (data.results && data.results.length > 0) {
const elevation = data.results[0].elevation;
console.log(`API获取海拔成功: ${elevation}米 (${lng}, ${lat})`);
return elevation;
}
return 0;
} catch (error) {
console.warn("OpenElevation API 获取海拔失败:", error);
// 尝试备用API
return await getElevationByBackupAPI(lng, lat);
}
}
/**
* 备用API方案: 使用其他高程API服务
* @param {number} lng - 经度
* @param {number} lat - 纬度
* @returns {Promise<number>} 返回海拔高度
*/
async function getElevationByBackupAPI(lng, lat) {
try {
// 备用方案1: 使用 elevation-api.io需要注册但免费额度较大
// const response = await fetch(
// `https://api.elevation-api.io/api/v1/lookup?locations=${lat},${lng}`
// );
// 备用方案2: 使用 Google Elevation API需要API密钥
// const response = await fetch(
// `https://maps.googleapis.com/maps/api/elevation/json?locations=${lat},${lng}&key=YOUR_API_KEY`
// );
// 备用方案3: 使用 MapBox Elevation API需要API密钥
// const response = await fetch(
// `https://api.mapbox.com/v4/mapbox.terrain-rgb/${lng},${lat}.json?access_token=YOUR_ACCESS_TOKEN`
// );
// 如果以上API都不可用返回0
return 0;
} catch (error) {
console.warn("备用API获取海拔失败:", error);
return 0;
}
}
/**
* 方案2: 使用 mars3d/Cesium 的地形采样需要地形服务已加载
* @param {number} lng - 经度
* @param {number} lat - 纬度
* @returns {Promise<number>} 返回海拔高度
*/
async function getElevationByTerrain(lng, lat) {
try {
if (!map || !map.scene) {
console.warn("地图未初始化");
return 0;
}
const Cesium = window.mars3d.Cesium;
const cartographic = Cesium.Cartographic.fromDegrees(lng, lat);
const terrainProvider = map.scene.terrainProvider;
if (!terrainProvider) {
console.warn("地形提供者未加载");
return 0;
}
// 等待地形提供者准备就绪
if (terrainProvider.readyPromise) {
await terrainProvider.readyPromise;
}
// 使用 sampleTerrainMostDetailed
const positions = [cartographic];
const updatedPositions = await Cesium.sampleTerrainMostDetailed(
terrainProvider,
positions
);
if (
updatedPositions &&
updatedPositions.length > 0 &&
updatedPositions[0].height !== undefined &&
updatedPositions[0].height !== null
) {
const elevation = updatedPositions[0].height;
console.log(
`地形采样获取海拔成功: ${elevation}米 (${lng}, ${lat})`
);
return elevation;
}
return 0;
} catch (error) {
console.warn("地形采样获取海拔失败:", error);
return 0;
}
}
/**
* 方案3: 使用 Cesium pickPosition 方法需要场景已渲染
* @param {number} lng - 经度
* @param {number} lat - 纬度
* @returns {Promise<number>} 返回海拔高度
*/
async function getElevationByPickPosition(lng, lat) {
try {
if (!map || !map.scene) {
console.warn("地图未初始化");
return 0;
}
const Cesium = window.mars3d.Cesium;
const cartesian = Cesium.Cartesian3.fromDegrees(lng, lat, 10000);
const cartographic = Cesium.Cartographic.fromCartesian(cartesian);
// 使用 pickPosition 获取地形高度
const pickedPosition = map.scene.pickPosition(cartesian);
if (pickedPosition) {
const pickedCartographic =
Cesium.Cartographic.fromCartesian(pickedPosition);
const elevation = pickedCartographic.height;
console.log(
`pickPosition获取海拔成功: ${elevation}米 (${lng}, ${lat})`
);
return elevation;
}
return 0;
} catch (error) {
console.warn("pickPosition获取海拔失败:", error);
return 0;
}
}
/**
* 方案4: 使用 mars3d 的工具方法如果可用
* @param {number} lng - 经度
* @param {number} lat - 纬度
* @returns {Promise<number>} 返回海拔高度
*/
async function getElevationByMars3dUtil(lng, lat) {
try {
if (!map || !map.scene) {
console.warn("地图未初始化");
return 0;
}
const mars3d = window.mars3d;
const Cesium = window.mars3d.Cesium;
// 尝试使用 mars3d 的工具方法
if (mars3d.PointUtil && mars3d.PointUtil.getTerrainHeight) {
const height = await mars3d.PointUtil.getTerrainHeight(
map.scene,
lng,
lat
);
if (height !== undefined && height !== null) {
console.log(
`mars3d工具获取海拔成功: ${height}米 (${lng}, ${lat})`
);
return height;
}
}
return 0;
} catch (error) {
console.warn("mars3d工具获取海拔失败:", error);
return 0;
}
}
/**
* 综合方案: 按优先级尝试多种方法获取海拔
* @param {number} lng - 经度
* @param {number} lat - 纬度
* @returns {Promise<number>} 返回海拔高度
*/
async function getElevationByLngLat(lng, lat) {
// 方案1: 优先使用第三方 API最可靠不依赖地形服务
let elevation = await getElevationByAPI(lng, lat);
if (elevation > 0) {
return elevation;
}
// 方案2: 尝试使用 mars3d 工具方法
elevation = await getElevationByMars3dUtil(lng, lat);
if (elevation > 0) {
return elevation;
}
// 方案3: 尝试使用地形采样
elevation = await getElevationByTerrain(lng, lat);
if (elevation > 0) {
return elevation;
}
// 方案4: 尝试使用 pickPosition
elevation = await getElevationByPickPosition(lng, lat);
if (elevation > 0) {
return elevation;
}
// 所有方法都失败返回0
console.warn(
`所有方法都无法获取坐标(${lng}, ${lat})的海拔高度返回默认值0`
);
return 0;
}
const mapConfig = {
scene: {
center: {
@ -375,105 +684,6 @@ function getLocationInfo(id) {
);
}
// // 百度地图初始化
// async function initMap() {
// // if (map) {
// // map.destroy();
// // map = null;
// // }
// // if (!mars3d.Util.webglreport()) {
// // mars3d.Util.webglerror();
// // }
// // try {
// // mapConfig.scene.center.lat = intLat || 31.686288;
// // mapConfig.scene.center.lng = intLng || 117.229619;
// // map = new mars3d.Map("map-box", mapConfig);
// // let graphicLayer = new mars3d.layer.GraphicLayer();
// // map.addLayer(graphicLayer);
// // addAllMapPoints(graphicLayer);
// // addMapLine(graphicLayer);
// // addMapAThousandFields(graphicLayer);
// // } catch (error) {
// // console.log("初始化地图出错", error);
// // haoutil.alert(error?.message, "出错了");
// // }
// try {
// // 1. 彻底清理旧地图实例
// if (map) {
// // 移除所有自定义图层
// if (graphicLayer) {
// graphicLayerList.forEach((e) => {
// graphicLayer.removeGraphic(e);
// });
// // graphicLayer.clear();
// // map.removeLayer(graphicLayer, true); // true表示彻底销毁
// // graphicLayer = null;
// }
// // 销毁地图实例
// // map.destroy();
// // map = null;
// // // 强制垃圾回收(非必要但建议)
// // if (window.gc) window.gc();
// // 5. 添加内容
// const centerPoint = [intLng, intLat];
// // 方式1直接飞向目标点
// map.flyToPoint(centerPoint, {
// radius: 5000, // 可视范围半径(米)
// duration: 2, // 飞行时间(秒)
// heading: 0, // 视角方向0-360度
// pitch: -45, // 俯仰角度(-90俯视0平视90仰视
// });
// await addAllMapPoints();
// await addMapLine();
// await addMapAThousandFields();
// } else {
// // 2. WebGL兼容性检查
// if (!mars3d.Util.webglreport()) {
// mars3d.Util.webglerror();
// return;
// }
// // 3. 初始化新地图
// mapConfig.scene.center.lat = intLat || 31.686288;
// mapConfig.scene.center.lng = intLng || 117.229619;
// map = new mars3d.Map("map-box", mapConfig);
// // 4. 创建新的图形图层
// graphicLayer = new mars3d.layer.GraphicLayer();
// const centerPoint = [intLng, intLat];
// map.flyToPoint(centerPoint, {
// radius: 5000, // 可视范围半径(米)
// duration: 5, // 飞行时间(秒)
// heading: 0, // 视角方向0-360度
// pitch: -45, // 俯仰角度(-90俯视0平视90仰视
// });
// map.addLayer(graphicLayer);
// // 5. 添加内容
// await addAllMapPoints();
// await addMapLine();
// await addMapAThousandFields();
// }
// } catch (error) {
// // 失败时也清理残留
// if (map) {
// map.destroy();
// map = null;
// }
// }
// }
async function initMap() {
const mars3d = window.mars3d; // 静态资源引入时对象都是挂载在window中
const Cesium = window.mars3d.Cesium;
@ -484,6 +694,20 @@ async function initMap() {
graphicLayerList.forEach((e) => {
graphicLayer.removeGraphic(e);
});
polylines1.forEach((e) => {
graphicLayer.removeGraphic(e);
});
polylines2.forEach((e) => {
graphicLayer.removeGraphic(e);
});
polylines3.forEach((e) => {
graphicLayer.removeGraphic(e);
});
polylines1 = [];
polylines2 = [];
polylines3 = [];
}
const centerPoint = [intLng, intLat];
@ -679,7 +903,55 @@ async function addAllMapPoints(graphicLayer, mars3d, Cesium) {
// 绘制杆塔
if (towerList && towerList.length > 0) {
towerList.forEach((pointData, index) => {
// showData(towerList, graphicLayer);
const towerList1 = towerList;
// 异步获取所有点的海拔高度
// const hList = [
// 1719.018, 1692.086, 1603.903, 1613.565, 1766.005, 1731.971, 1740.48,
// 1751.94, 1762.712, 1711.249,
// ];
const newTowerList2D = towerList1.filter(
(item) =>
item.towerProgress != 6 &&
item.towerProgress != 7 &&
item.towerProgress != 8 &&
item.towerProgress != 9 &&
item.towerProgress != 10
);
const newTowerList3D = towerList1.filter(
(item) =>
item.towerProgress == 6 ||
item.towerProgress == 7 ||
item.towerProgress == 8 ||
item.towerProgress == 9 ||
item.towerProgress == 10
);
const newTowerList3DData = await Promise.all(
newTowerList3D.map(async (item, index) => {
const altitude = await getElevationByLngLat(
item.baiduLon,
item.baiduLat
);
return {
alt: altitude - 56, // 海拔高度 根据经纬度计算
heading: 23.0667, // 方向
headingStr: "23°4", // 方向字符串
height: 56, // 杆塔高度
lat: item.baiduLat, // 纬度
lon: item.baiduLon, // 经度
...item,
};
})
);
// 过滤出需要绘制3D塔的数据
showData(newTowerList3DData, graphicLayer);
newTowerList2D.forEach((pointData, index) => {
const isEven = index % 2 === 0;
// 标点垂直方向设置
const billboardVerticalOrigin = Cesium.VerticalOrigin.BOTTOM; // 标点固定底部对齐
@ -932,21 +1204,29 @@ async function addAllMapPoints(graphicLayer, mars3d, Cesium) {
// 添加标点折线(智能绘制虚实线)
async function addMapLine(graphicLayer, mars3d, Cesium) {
const newTowerList1 = towerList.filter(
(item) =>
item.towerProgress != 6 &&
item.towerProgress != 7 &&
item.towerProgress != 8 &&
item.towerProgress != 9 &&
item.towerProgress != 10
);
console.log(newTowerList1, "newTowerList1----");
// 存储所有线段
const segments = [];
// 绘制杆塔线
if (towerList.length > 0) {
for (let i = 0; i < towerList.length - 1; i++) {
const startPoint = towerList[i];
const endPoint = towerList[i + 1];
for (let i = 0; i < newTowerList1.length - 1; i++) {
const startPoint = newTowerList1[i];
const endPoint = newTowerList1[i + 1];
let lineColor = "";
let lineMaterial = null; // 用于存储线材质
// 判断条件部分保持不变
if (
(startPoint.towerProgress == 8 &&
endPoint.towerProgress == 8) ||
(startPoint.towerProgress == 8 && endPoint.towerProgress == 8) ||
startPoint.towerProgress == 8
) {
// lineColor = "#FFFF00";
@ -1004,8 +1284,7 @@ async function addMapLine(graphicLayer, mars3d, Cesium) {
// graphicLayer.addGraphic(polyline);
// graphicLayerList.push(polyline);
} else if (
(startPoint.towerProgress === 9 &&
endPoint.towerProgress === 9) ||
(startPoint.towerProgress === 9 && endPoint.towerProgress === 9) ||
(startPoint.towerProgress === 10 &&
endPoint.towerProgress === 10) ||
startPoint.towerProgress === 9
@ -1042,7 +1321,6 @@ async function addMapLine(graphicLayer, mars3d, Cesium) {
});
}
}
}
// 绘制交叉线路
if (crossingLineListNew.length > 0) {
@ -1254,6 +1532,275 @@ async function addMapAThousandFields(graphicLayer, mars3d, Cesium) {
});
}
}
function showData(arrdata, graphicLayer) {
const polylinesTB = []; // 图标显示的点
// 预处理坐标及角度
for (let i = 0, len = arrdata.length; i < len; i++) {
const item = arrdata[i];
const position = Cesium.Cartesian3.fromDegrees(
item.lon,
item.lat,
item.alt
);
item.position = position;
item.index = i + 1;
// 模型比例,根据塔高换算
item.scale = item.height / 52;
// 测试塔顶高度与实际高度是否一致
// const positionTop = mars3d.PointUtil.addPositionsHeight(position, item.height) // 顶部点
// const graphic2 = new mars3d.graphic.PointPrimitive({
// position: positionTop,
// style: {
// color: "#ff0000",
// pixelSize: 8,
// outlineColor: "#ffffff",
// outlineWidth: 2
// }
// })
// graphicLayer.addGraphic(graphic2)
// 计算电线塔转角角度
if (i !== 0) {
const priorPt = arrdata[i - 1].position;
item.lineHeading = mars3d.MeasureUtil.getAngle(priorPt, position); // 线的角度
}
}
// 计算各坐标及路线坐标,并渲染矢量对象
for (let i = 0, len = arrdata.length; i < len; i++) {
const item = arrdata[i];
const position = item.position;
// 计算电线塔转角角度
let degree = -2.2333;
// if (degree) {
// item.degree = item.lineHeading - item.heading
// } else {
if (i === 0) {
degree = arrdata[i + 1].lineHeading;
} else if (i === len - 1) {
degree = arrdata[i].lineHeading;
} else {
const nextTower = arrdata[i + 1];
let stepAngle = (nextTower.lineHeading - item.lineHeading) / 2;
if (stepAngle > 90) {
stepAngle = 180 - stepAngle;
} else if (stepAngle < -90) {
stepAngle = stepAngle + 180;
}
degree = item.lineHeading + stepAngle;
}
item.degree = degree;
// }
const hpr = new Cesium.HeadingPitchRoll(
Cesium.Math.toRadians(degree),
0,
0
);
// 3个悬垂串的位置
const offsetLineZ = item.height - 3.9;
let newPoint1 = mars3d.PointUtil.getPositionByHprAndOffset(
position,
new Cesium.Cartesian3(0, 10.6, offsetLineZ),
hpr
); // 左边挂线
let newPoint2 = mars3d.PointUtil.getPositionByHprAndOffset(
position,
new Cesium.Cartesian3(0, 0, offsetLineZ),
hpr
); // 中间线
let newPoint3 = mars3d.PointUtil.getPositionByHprAndOffset(
position,
new Cesium.Cartesian3(0, -10.6, offsetLineZ),
hpr
); // 右边挂线
// polylinesTB.push(newPoint2); // 图标显示的点
drawWireTowerModel(graphicLayer, position, degree, item.scale, item);
drawWireTowerJYZModel(newPoint1, degree, graphicLayer); // 3个悬垂串模型
drawWireTowerJYZModel(newPoint2, degree, graphicLayer);
drawWireTowerJYZModel(newPoint3, degree, graphicLayer);
// 计算路线点
const jyzHeight = -5;
newPoint1 = mars3d.PointUtil.addPositionsHeight(newPoint1, jyzHeight);
newPoint2 = mars3d.PointUtil.addPositionsHeight(newPoint2, jyzHeight);
newPoint3 = mars3d.PointUtil.addPositionsHeight(newPoint3, jyzHeight);
if (i === 0) {
polylines1.push(newPoint1);
polylines2.push(newPoint2);
polylines3.push(newPoint3);
} else {
const angularityFactor = -5000;
const num = 50;
let positions = mars3d.PolyUtil.getLinkedPointList(
polylines1[polylines1.length - 1],
newPoint1,
angularityFactor,
num
); // 计算曲线点
polylines1 = polylines1.concat(positions);
positions = mars3d.PolyUtil.getLinkedPointList(
polylines2[polylines2.length - 1],
newPoint2,
angularityFactor,
num
); // 计算曲线点
polylines2 = polylines2.concat(positions);
positions = mars3d.PolyUtil.getLinkedPointList(
polylines3[polylines3.length - 1],
newPoint3,
angularityFactor,
num
); // 计算曲线点
polylines3 = polylines3.concat(positions);
}
}
// 绘制路线
drawGuideLine(polylines1, "#0000ff", graphicLayer);
drawGuideLine(polylines2, "#cccccc", graphicLayer);
drawGuideLine(polylines3, "#ff0000", graphicLayer);
polylines1 = mars3d.LngLatArray.toArray(polylines1);
polylines2 = mars3d.LngLatArray.toArray(polylines2);
polylines3 = mars3d.LngLatArray.toArray(polylines3);
}
// 绘制电线塔模型
function drawWireTowerModel(graphicLayer, position, degree, scale, item) {
console.log(item, "item----123");
const item1 = {
alt: 488.28,
heading: -2.2333,
headingStr: "-2°14",
height: 52,
lat: item.baiduLat,
lon: item.baiduLon,
};
// const html = mars3d.Util.getTemplateHtml({
// title: "塔杆",
// template: [
// { field: "towerName", name: "杆塔名称" },
// { field: "time1", name: "协调完成" },
// { field: "time2", name: "基础开挖" },
// { field: "height", name: "杆塔高度" },
// { field: "alt", name: "海拔高度" },
// ],
// attr: item,
// });
const graphic = new mars3d.graphic.ModelPrimitive({
position,
style: {
url: "https://data.mars3d.cn/gltf/mars/tower/tower-500kV.glb",
heading: degree,
scale: scale,
distanceDisplayCondition: new Cesium.DistanceDisplayCondition(
0,
4000.0
),
},
// popup: html,
});
// 添加点击事件
graphic.on(mars3d.EventType.click, function (event) {
// console.log("点击了标点", event.graphic.attr);
const graphic = event.graphic;
let infoContent = `
<div class="map-container">
<h4>${item.towerName}</h4>
<div class="map-container-item">
<span>协调完成</span>
<span>${item.time1 || "/"}</span>
</div>
<div class="map-container-item">
<span>基础开挖</span>
<span>${item.time2 || "/"}</span>
</div>
<div class="map-container-item">
<span>基础开挖完成</span>
<span>${item.time3 || "/"}</span>
</div>
<div class="map-container-item">
<span>基础浇筑</span>
<span>${item.time4 || "/"}</span>
</div>
<div class="map-container-item">
<span>基础浇筑完成</span>
<span>${item.time5 || "/"}</span>
</div>
<div class="map-container-item">
<span>铁塔组立</span>
<span>${item.time6 || "/"}</span>
</div>
<div class="map-container-item">
<span>铁塔组立完成</span>
<span>${item.time7 || "/"}</span>
</div>
<div class="map-container-item">
<span>架线施工</span>
<span>${item.time8 || "/"}</span>
</div>
<div class="map-container-item">
<span>架线施工完成</span>
<span>${item.time9 || "/"}</span>
</div>
<div class="map-container-item">
<span>附件安装完成</span>
<span>${item.time10 || "/"}</span>
</div>
</div>`;
// 创建自定义信息窗体
graphicLayer.bindPopup(infoContent, {
position: graphic.position,
});
});
graphicLayer.addGraphic(graphic);
graphicLayerList.push(graphic);
}
function drawWireTowerJYZModel(position, degree, graphicLayer) {
const graphic2 = new mars3d.graphic.ModelPrimitive({
position,
style: {
url: "https://data.mars3d.cn/gltf/mars/tower/tower-jyz.glb",
heading: degree,
pitch: 90, // 模型本身不是竖直需要加pitch纠正
scale: 1,
distanceDisplayCondition: new Cesium.DistanceDisplayCondition(
0,
4000.0
),
},
});
graphicLayer.addGraphic(graphic2);
graphicLayerList.push(graphic2);
}
function drawGuideLine(positions, color, graphicLayer) {
const graphic = new mars3d.graphic.PolylinePrimitive({
positions,
style: {
width: 4,
color,
},
});
graphicLayer.addGraphic(graphic);
graphicLayerList.push(graphic);
}
// 获取组织树数据
function getOrgTreeData() {
ajaxRequest(

File diff suppressed because it is too large Load Diff