Files
yjearth/index.html
2025-09-08 17:01:50 +08:00

439 lines
16 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Cesium 多资源整合加载平台</title>
<!-- 引入Cesium库使用1.116稳定版本) -->
<script src="https://cesium.com/downloads/cesiumjs/releases/1.116/Build/Cesium/Cesium.js"></script>
<link href="https://cesium.com/downloads/cesiumjs/releases/1.116/Build/Cesium/Widgets/widgets.css" rel="stylesheet">
<style>
/* 基础样式重置 */
html, body, #cesiumContainer {
width: 100%;
height: 100%;
margin: 0;
padding: 0;
overflow: hidden;
}
/* 控制面板样式 */
.control-panel {
position: absolute;
top: 20px;
left: 20px;
background: rgba(255, 255, 255, 0.95);
padding: 20px;
border-radius: 8px;
box-shadow: 0 3px 15px rgba(0, 0, 0, 0.15);
z-index: 100;
width: 420px;
}
/* 表单标题 */
.panel-title {
margin: 0 0 15px;
font-size: 18px;
color: #2c3e50;
border-bottom: 1px solid #eee;
padding-bottom: 8px;
}
/* 表单分组样式 */
.form-group {
margin-bottom: 15px;
}
.form-group label {
display: block;
margin-bottom: 6px;
font-weight: 500;
color: #34495e;
}
/* 输入框、选择框样式 */
.form-control {
width: 100%;
padding: 10px;
border: 1px solid #ddd;
border-radius: 4px;
font-size: 14px;
box-sizing: border-box;
transition: border-color 0.3s;
}
.form-control:focus {
outline: none;
border-color: #0070f3;
box-shadow: 0 0 0 2px rgba(0, 112, 243, 0.1);
}
/* 经纬度输入行布局 */
.coord-group {
display: flex;
gap: 10px;
}
.coord-group .form-group {
flex: 1;
}
/* 按钮样式 */
.btn-group {
display: flex;
gap: 10px;
margin-top: 10px;
}
.btn {
padding: 10px 16px;
border: none;
border-radius: 4px;
font-size: 14px;
cursor: pointer;
transition: background-color 0.3s;
}
.btn-primary {
background-color: #0070f3;
color: white;
}
.btn-primary:hover {
background-color: #0051aa;
}
.btn-locate {
background-color: #22c55e;
color: white;
}
.btn-locate:hover {
background-color: #16a34a;
}
.btn-clear {
background-color: #f37000;
color: white;
}
.btn-clear:hover {
background-color: #d96000;
}
/* 状态提示样式 */
.status {
margin-top: 12px;
padding: 10px;
border-radius: 4px;
font-size: 14px;
display: none; /* 默认隐藏 */
}
.status-success {
background-color: #e8f5e9;
color: #2e7d32;
display: block; /* 成功时显示 */
}
.status-error {
background-color: #fdecea;
color: #d32f2f;
display: block; /* 错误时显示 */
}
/* 鼠标经纬度提示框样式 */
.latlng-tooltip {
position: absolute;
background: rgba(0, 0, 0, 0.8);
color: white;
padding: 6px 10px;
border-radius: 4px;
font-size: 12px;
pointer-events: none; /* 避免遮挡鼠标事件 */
z-index: 1000; /* 确保在最上层显示 */
opacity: 0; /* 默认透明 */
transition: opacity 0.2s; /* 淡入淡出效果 */
white-space: nowrap; /* 防止文本换行 */
}
</style>
</head>
<body>
<!-- Cesium 容器 -->
<div id="cesiumContainer"></div>
<!-- 鼠标经纬度提示框 -->
<div id="latlngTooltip" class="latlng-tooltip"></div>
<!-- 控制表单面板 -->
<div class="control-panel">
<h3 class="panel-title">Cesium 资源加载与定位</h3>
<!-- 基础地址输入项 -->
<div class="form-group">
<label for="baseUrl">基础地址(服务器根地址)</label>
<input type="text" id="baseUrl" class="form-control"
placeholder="例: http://192.168.110.25:8848"
value="http://192.168.110.25:8848">
</div>
<!-- 资源地址输入 -->
<div class="form-group">
<label for="resourceUrl">资源相对路径</label>
<input type="text" id="resourceUrl" class="form-control"
placeholder="例: 1.倾斜模型: data/clt/.../tileset.json 2.高程: terrain_pak 3.瓦片: tiles/{z}/{x}/{y}.png"
value="/data/pak/8870a7a573dc621d7347457a5497df3b/{z}/{x}/{y}.png">
</div>
<!-- 经纬度定位输入 -->
<div class="coord-group">
<div class="form-group">
<label for="longitude">经度</label>
<input type="text" id="longitude" class="form-control"
placeholder="范围: -180 ~ 180" value="106.253200504443">
</div>
<div class="form-group">
<label for="latitude">纬度</label>
<input type="text" id="latitude" class="form-control"
placeholder="范围: -90 ~ 90" value="29.8500521523625">
</div>
</div>
<!-- 操作按钮组 -->
<div class="btn-group">
<button id="loadBtn" class="btn btn-primary">加载资源</button>
<button id="locateBtn" class="btn btn-locate">定位到经纬度</button>
<button id="clearBtn" class="btn btn-clear">清除所有资源</button>
</div>
<!-- 状态提示 -->
<div id="status" class="status"></div>
</div>
<script>
// 1. 初始化Cesium核心配置
Cesium.Ion.defaultAccessToken = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiI5MGU0NGMwYS00ZDBkLTQzMDItYjc5Zi0zYTM1NDcwZGVjMmEiLCJpZCI6MTU1MTk5LCJpYXQiOjE2ODk4MTgzNTF9.V_eZ5KlAI9qmxsDivT6pMC3Pq6qLk0mXpBoe5C0Mm4g';
// 2. 创建Cesium Viewer基础配置
const viewer = new Cesium.Viewer('cesiumContainer', {
animation: false,
timeline: false,
vrButton: false,
infoBox: false,
selectionIndicator: false,
homeButton: true,
sceneModePicker: true,
navigationHelpButton: true,
fullscreenButton: true,
imageryProvider: false
});
// 隐藏Cesium版权信息
viewer._cesiumWidget._creditContainer.style.display = 'none';
// 3. 全局变量
let loadedResource = { type: null, instance: null };
const statusEl = document.getElementById('status');
const latlngTooltip = document.getElementById('latlngTooltip');
// 4. 工具函数: 拼接资源URL
function getFullResourceUrl() {
const baseUrl = document.getElementById('baseUrl').value.trim();
const resourceUrl = document.getElementById('resourceUrl').value.trim();
if (!resourceUrl) return '';
if (!baseUrl) return resourceUrl;
const baseUrlWithSlash = baseUrl.endsWith('/') ? baseUrl : `${baseUrl}/`;
const cleanResourceUrl = resourceUrl.startsWith('/') ? resourceUrl.slice(1) : resourceUrl;
return `${baseUrlWithSlash}${cleanResourceUrl}`;
}
// 5. 工具函数: 识别资源类型
function getResourceTypeByUrl(fullUrl) {
const lowerUrl = fullUrl.toLowerCase();
if (lowerUrl.includes('.png') || lowerUrl.includes('.jpg')) return 'imagery';
if (lowerUrl.includes('tileset.json')) return 'tileset';
if (lowerUrl.includes('pak')) return 'terrain';
return null;
}
// 6. 状态提示函数
function showStatus(message, isSuccess = true) {
statusEl.textContent = message;
statusEl.className = 'status';
statusEl.classList.add(isSuccess ? 'status-success' : 'status-error');
if (isSuccess) {
setTimeout(() => {
statusEl.classList.remove('status-success');
statusEl.style.display = 'none';
}, 5000);
}
}
// 7. 清除资源函数
function clearLoadedResource() {
if (!loadedResource.instance) return;
switch (loadedResource.type) {
case 'tileset':
viewer.scene.primitives.remove(loadedResource.instance);
showStatus('倾斜模型已清除');
break;
case 'imagery':
viewer.imageryLayers.remove(loadedResource.instance);
showStatus('二维瓦片已清除');
break;
case 'terrain':
viewer.terrainProvider = new Cesium.EllipsoidTerrainProvider();
viewer.scene.globe.depthTestAgainstTerrain = false;
showStatus('高程模型已清除');
break;
}
loadedResource = { type: null, instance: null };
}
// 8. 资源加载函数
async function loadResource() {
const fullUrl = getFullResourceUrl();
if (!fullUrl) {
showStatus('请输入有效的资源相对路径', false);
return;
}
const resourceType = getResourceTypeByUrl(fullUrl);
if (!resourceType) {
showStatus('无法识别资源类型、请检查路径是否包含tileset.json/pak/.png/.jpg', false);
return;
}
clearLoadedResource();
try {
let instance;
switch (resourceType) {
case 'tileset':
showStatus('正在加载倾斜模型...');
instance = await Cesium.Cesium3DTileset.fromUrl(fullUrl);
viewer.scene.primitives.add(instance);
viewer.zoomTo(instance);
showStatus('倾斜模型加载成功');
break;
case 'imagery':
showStatus('正在加载二维瓦片...');
const fileExtension = fullUrl.toLowerCase().includes('.png') ? 'png' : 'jpg';
const provider = new Cesium.UrlTemplateImageryProvider({
url: fullUrl,
fileExtension: fileExtension,
minimumLevel: 0,
maximumLevel: 18,
credit: '自定义二维瓦片'
});
instance = viewer.imageryLayers.addImageryProvider(provider);
showStatus('二维瓦片加载成功');
break;
case 'terrain':
showStatus('正在加载高程模型...');
instance = await Cesium.CesiumTerrainProvider.fromUrl(fullUrl);
viewer.terrainProvider = instance;
viewer.scene.globe.depthTestAgainstTerrain = true;
showStatus('高程模型加载成功');
break;
}
loadedResource = { type: resourceType, instance: instance };
} catch (error) {
console.error('资源加载失败: ', error);
const errorMsg = error.message.includes('404') ? '资源地址不存在404' :
error.message.includes('CORS') ? '跨域访问被拒绝CORS' :
'服务器连接失败或资源格式错误';
showStatus(`资源加载失败: ${errorMsg}`, false);
}
}
// 9. 经纬度定位函数
function flyToCoordinate() {
const lng = parseFloat(document.getElementById('longitude').value.trim());
const lat = parseFloat(document.getElementById('latitude').value.trim());
if (isNaN(lng) || isNaN(lat) || lng < -180 || lng > 180 || lat < -90 || lat > 90) {
showStatus('请输入有效的经纬度(经度: -180~180、纬度: -90~90', false);
return;
}
viewer.camera.flyTo({
destination: Cesium.Cartesian3.fromDegrees(lng, lat, 10000),
duration: 2,
orientation: {
heading: Cesium.Math.toRadians(0),
pitch: Cesium.Math.toRadians(-30),
roll: 0
}
});
showStatus(`已定位到经纬度: (${lng.toFixed(4)}, ${lat.toFixed(4)})`);
}
// 10. 修复: 鼠标悬浮显示经纬度(支持地球+地形表面、避免NaN
function initLatlngTooltip() {
const canvas = viewer.scene.canvas;
// 鼠标移动事件
canvas.addEventListener('mousemove', (e) => {
// 1. 先尝试获取地形表面坐标(优先、因为加载高程后更准确)
const windowPosition = new Cesium.Cartesian2(e.clientX, e.clientY);
let cartographic = null;
// 方法1: 从地形表面获取坐标(适用于加载了高程模型的场景)
const ray = viewer.camera.getPickRay(windowPosition);
if (ray) {
const hitResult = viewer.scene.pickFromRay(ray);
if (hitResult && hitResult.position) {
cartographic = Cesium.Cartographic.fromCartesian(hitResult.position);
}
}
// 方法2: 如果地形获取失败、从椭球面获取(适用于无高程的场景)
if (!cartographic) {
cartographic = viewer.scene.camera.pickEllipsoid(windowPosition);
}
// 3. 计算并显示经纬度(增加异常处理)
if (cartographic && !isNaN(cartographic.longitude) && !isNaN(cartographic.latitude)) {
const longitude = Cesium.Math.toDegrees(cartographic.longitude).toFixed(6);
const latitude = Cesium.Math.toDegrees(cartographic.latitude).toFixed(6);
// 显示经纬度避免NaN
latlngTooltip.textContent = `经度: ${longitude} | 纬度: ${latitude}`;
// 调整提示框位置(防止超出屏幕)
const tooltipWidth = latlngTooltip.offsetWidth || 150; // 预估宽度
const left = e.clientX + 10 > window.innerWidth - tooltipWidth
? e.clientX - tooltipWidth - 10
: e.clientX + 10;
const top = e.clientY + 10 > window.innerHeight - 30
? e.clientY - 30
: e.clientY + 10;
latlngTooltip.style.left = `${left}px`;
latlngTooltip.style.top = `${top}px`;
latlngTooltip.style.opacity = '1';
} else {
// 非地球表面时隐藏提示框
latlngTooltip.style.opacity = '0';
}
});
// 鼠标离开画布时隐藏
canvas.addEventListener('mouseleave', () => {
latlngTooltip.style.opacity = '0';
});
}
// 11. 绑定按钮事件
document.getElementById('loadBtn').addEventListener('click', loadResource);
document.getElementById('locateBtn').addEventListener('click', flyToCoordinate);
document.getElementById('clearBtn').addEventListener('click', () => {
clearLoadedResource();
if (viewer.imageryLayers.length > 0) {
viewer.imageryLayers.removeAll();
showStatus('所有影像图层已清除');
}
});
// 12. 初始化鼠标经纬度提示
initLatlngTooltip();
</script>
</body>
</html>