赞
踩
示例代码下载:https://gitee.com/yang-zuo/cesium_-historical_-imagery.git
源码地址
谷歌地球历史影像,在cesium中加载谷歌地球历史影像
在实际的项目开发中,影像、地形是所有地理信息相关可视化的基础数据。
TileSer 离线地图服务系统,可以用于在内网发布地图影像、历史影像、地形高程、3D Tiles 模型、Osgb 模型、街景等。
其他内容,详见 API 文档。
本文将以 Cesium 为例,讲解如何调用调用无水印历史高清影像。
三维引擎:Cesium 所有版本
游戏引擎:Unreal
其他:其他二维码等平台,目前没有主动适配(用处较小,接受定制)
本示例将展示 Cesium 如何添加历史影像服务。
说明:最新影像和无水印默认影像有何区别?
最新影像,默认加载的是最新影像。不考虑清晰度、云层等遮挡情况。
默认影像,会考虑清晰度、云层遮挡,默认选择最高清和优质的影像。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>XbsjTileserImageryProvider示例</title> <script type="text/javascript" src="https://cesium.com/downloads/cesiumjs/releases/1.112/Build/Cesium/Cesium.js"></script> <script src="./tileserHis.js"></script> <link rel="stylesheet" href="https://cdn.bootcdn.net/ajax/libs/cesium/1.109.0/Widgets/widgets.css"> <style> html, body, #cesiumContainer { width: 100%; height: 100%; margin: 0; padding: 0; overflow: hidden; } .dateDropdownContainer { position: absolute; z-index: 99999; top: 60px; /* 调整这个值以避免与按钮组重叠 */ left: 20px; } </style> </head>
<body> <div id="cesiumContainer"> <div id="dateDropdownContainer" class="dateDropdownContainer" style="display: none;"> <select id="dateDropdown"></select> </div> </div> <script> const viewer = new Cesium.Viewer('cesiumContainer', {}); // 全局变量,存储当前选中的时间 let currentSelectedTime; // 初始化地图和监听相机移动事件 updateImageryLayer(0); // 初始化图层 getAndDisplayInitialTimeData(); // 获取初始时间并显示 let isCameraMoving = false; viewer.camera.moveStart.addEventListener(() => { isCameraMoving = true; }); viewer.camera.moveEnd.addEventListener(async () => { if (isCameraMoving) { isCameraMoving = false; const allTimes = await getCurrentTileCoordinates(viewer); displayDatesInDropdown(allTimes); } }); // 更新图层的函数 function updateImageryLayer(decimalTime) { viewer.imageryLayers.removeAll(); // 移除所有现有的图层 var imgLayer = new XbsjTileserHisImageryProvider({ indexTime: decimalTime }); viewer.imageryLayers.addImageryProvider(imgLayer); } // 显示下拉菜单的函数 function displayDatesInDropdown(allTimes) { const dropdown = document.getElementById('dateDropdown'); dropdown.innerHTML = ''; // 清空现有选项 let closestIndex = -1; let closestDiff = Infinity; // 函数:计算两个日期字符串之间的天数差异 function getDaysDifference(dateStr1, dateStr2) { const date1 = new Date(dateStr1); const date2 = new Date(dateStr2); const diffTime = Math.abs(date2 - date1); return diffTime / (1000 * 60 * 60 * 24); } allTimes.forEach((time, index) => { const option = document.createElement('option'); option.value = time; option.textContent = time; dropdown.appendChild(option); // 寻找与 currentSelectedTime 最接近的时间 const diff = getDaysDifference(time, currentSelectedTime); if (diff < closestDiff) { closestDiff = diff; closestIndex = index; } }); // 如果找到最接近的时间,则选择它 if (closestIndex !== -1) { dropdown.selectedIndex = closestIndex;// 更新当前选中的时间 } if (allTimes.length > 0) { document.getElementById('dateDropdownContainer').style.display = 'block'; } else { document.getElementById('dateDropdownContainer').style.display = 'none'; } dropdown.onchange = function () { currentSelectedTime = dropdown.value; // 更新当前选中的时间 updateImageryLayer(currentSelectedTime); }; } // 获取初始时间并显示的函数 async function getAndDisplayInitialTimeData() { const initialTimes = await getCurrentTileCoordinates(viewer); // 获取时间数据 displayDatesInDropdown(initialTimes); // 显示时间数据 } </script> </body>
//根据瓦片xyz值,获取瓦片的时间信息
async function xyzToAllInfo(x, y, z) {
const response = await fetch(`https://tileser.giiiis.com/xyzinfo/${z}/${x}/${y}`);
const jsonData = response.json();
return jsonData;
}
以 https://tileser.giiiis.com/xyzinfo/14/27317/5444 为例,获取所有时间信息。
结合上面的方法
//获取cesium视口范围的信息。 async function getCurrentTileCoordinates(viewer) { const scene = viewer.scene; const ellipsoid = scene.globe.ellipsoid; const camera = scene.camera; // 获取相机的经纬度 const cameraPositionCartographic = ellipsoid.cartesianToCartographic(camera.position); const longitude = Cesium.Math.toDegrees(cameraPositionCartographic.longitude); const latitude = Cesium.Math.toDegrees(cameraPositionCartographic.latitude); // 计算缩放级别 (Z) const cameraHeight = cameraPositionCartographic.height; const level = altToZoom(cameraHeight); // 转换经纬度为瓦片坐标 (X, Y) let x = Math.floor((longitude + 180) / 360 * Math.pow(2, level + 1)); let y = Math.floor((90 - latitude) / 180 * Math.pow(2, level)); //获取信息XYZ信息,查看时间进行返回 const allTimes = await xyzToAllInfo(x, y, level); return allTimes; } //3D级数转为2D高,根据 function altToZoom(cameraHeight) { const levels = [ { maxAlt: 250000000, level: 0 }, { maxAlt: 25000000, level: 1 }, { maxAlt: 9000000, level: 2 }, { maxAlt: 7000000, level: 3 }, { maxAlt: 4400000, level: 4 }, { maxAlt: 2000000, level: 5 }, { maxAlt: 1000000, level: 6 }, { maxAlt: 493977, level: 7 }, { maxAlt: 218047, level: 8 }, { maxAlt: 124961, level: 9 }, { maxAlt: 56110, level: 10 }, { maxAlt: 40000, level: 11 }, { maxAlt: 13222, level: 12 }, { maxAlt: 7000, level: 13 }, { maxAlt: 4000, level: 14 }, { maxAlt: 2500, level: 15 }, { maxAlt: 1500, level: 16 }, { maxAlt: 600, level: 17 }, { maxAlt: 250, level: 18 }, { maxAlt: 150, level: 19 }, { maxAlt: 50, level: 20 } ]; for (const { maxAlt, level } of levels) { if (cameraHeight >= maxAlt) { return level; } } return 20; // 默认级别 }
继承 UrlTemplateImageryProvider
主要是为了可以方便的改时间信息。如果不封装这个。直接修改 url 地址也是一样的。
class XbsjTileserHisImageryProvider extends Cesium.UrlTemplateImageryProvider { constructor(options) { // 设置默认的 minimumLevel 和 tilingScheme options.url = "https://tileser.giiiis.com/timetile/"; options.minimumLevel = 1; options.maximumLevel = 18; options.tilingScheme = new Cesium.GeographicTilingScheme(); super(options); // 初始化 indexTime 属性 this.indexTime = options.indexTime || 0; // 默认值为0 } async requestImage(x, y, level, request) { // 重写 requestImage 方法以支持异步操作 // 异步获取时间信息 try { // 根据获取的时间信息构建新的 URL let imageUrl = this.buildImageUrl(this.indexTime, x, y, level); return Cesium.ImageryProvider.loadImage(this, imageUrl); } catch (error) { return undefined; } } buildImageUrl(indexTime, x, y, level) { return `http://tileser.giiiis.com/timetile/${indexTime}/${level}/${x}/${y}.jpg`; } }
封装 XbsjTileserHisImageryProvider,就很方便修改 url 的信息。然后封装一个根据时间传参,穿件地图的方法,
function updateImageryLayer(decimalTime) {
viewer.imageryLayers.removeAll(); // 移除所有现有的图层
var imgLayer = new XbsjTileserHisImageryProvider({
indexTime: decimalTime
});
viewer.imageryLayers.addImageryProvider(imgLayer);
}
//创建插件中,历史影像核心对象XbsjTileserHisImageryProvider,配置indexTime为0,即可加载最新影像
//当传值为allTimes的数组的某一个值,如果"2021-9-28",即可修改时间。
请注意!请注意!
切换影像或者时间,请务必移除全部影像!
viewer.imageryLayers.removeAll(); // 移除所有现有的图层
简单来说就是根据相机变化,await getCurrentTileCoordinates(viewer);获取时间信息。然后交给下拉菜单进行展示。
// 全局变量,存储当前选中的时间和上一次选中的时间 let currentSelectedTime; updateImageryLayer(0); //初始化图层,默认为0,即刚开始默认加载最新时间 getAndDisplayInitialTimeData(); //获取初始时间并显示,这里主要作用直接获取全部时间,然后显示到下拉菜单的控件中 //该处,用户可以根据业务需要,重写displayDatesInDropdown函数。 //**// let isCameraMoving = false; viewer.camera.moveStart.addEventListener(() => { isCameraMoving = true; }); viewer.camera.moveEnd.addEventListener(async () => { if (isCameraMoving) { isCameraMoving = false; const allTimes = await getCurrentTileCoordinates(viewer); displayDatesInDropdown(allTimes); } }); //以上代码核心作用是监听相机改变事件,也就是当:相机位移,即获取最新的全部时间。然后displayDatesInDropdown进行判定和显示, //**// async function getAndDisplayInitialTimeData() { const initialTimes = await getCurrentTileCoordinates(viewer); // 获取时间数据 displayDatesInDropdown(initialTimes); // 显示时间数据 }
根据时间表,进行展示,同时!最关键的是,onchange 事件,也就是点击选择时间就切换整个地球的时间。
以下代码建议全保留
// 显示下拉菜单的函数 function displayDatesInDropdown(allTimes) { const dropdown = document.getElementById('dateDropdown'); dropdown.innerHTML = ''; // 清空现有选项 let closestIndex = -1; let closestDiff = Infinity; // 函数:计算两个日期字符串之间的天数差异 function getDaysDifference(dateStr1, dateStr2) { const date1 = new Date(dateStr1); const date2 = new Date(dateStr2); const diffTime = Math.abs(date2 - date1); return diffTime / (1000 * 60 * 60 * 24); } allTimes.forEach((time, index) => { const option = document.createElement('option'); option.value = time; option.textContent = time; dropdown.appendChild(option); // 寻找与 currentSelectedTime 最接近的时间 const diff = getDaysDifference(time, currentSelectedTime); if (diff < closestDiff) { closestDiff = diff; closestIndex = index; } }); // 如果找到最接近的时间,则选择它 if (closestIndex !== -1) { dropdown.selectedIndex = closestIndex;// 更新当前选中的时间 } if (allTimes.length > 0) { document.getElementById('dateDropdownContainer').style.display = 'block'; } else { document.getElementById('dateDropdownContainer').style.display = 'none'; } dropdown.onchange = function () { currentSelectedTime = dropdown.value; // 更新当前选中的时间 updateImageryLayer(currentSelectedTime); }; }
历史影像同样可以在 ue 中实现,不过,因为相对更加复杂。尤其是获取相机信息。这个必须要在 UE 里实现。
为了方便试用,代码已经封装到 EarthSDK,详见 https://earthsdk.com/
如果不在意,获取时间麻烦,UE 加载需要转为地图引擎,比如 cesium for Unreal、Unity。
地址:https://tileser.giiiis.com/timetile/tms/{时间数值}/tilemapresource.xml
将上面的地址,复制到 Cesium World Terrain 的 CesiumTileMapServiceRasterOverlay 的 url 中
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。