Files
yjearth/index.html

439 lines
16 KiB
HTML
Raw Permalink Normal View History

2025-09-08 17:01:50 +08:00
<!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>