WholeProcessPlatform/frontend/src/components/gis/map.cesium.ts

207 lines
6.8 KiB
TypeScript
Raw Normal View History

// src/components/gis/map.cesium.ts
import * as Cesium from 'cesium';
import type { MapInterface, layer } from './map.d';
// ✅ 1. 全局配置 Cesium 静态资源路径 (vite-plugin-cesium 通常会自动处理,但显式指定更保险)
// 注意:如果你使用了 vite-plugin-cesium这一步通常不需要但如果报错可以尝试取消注释下面这行
// Cesium.buildModuleUrl.setBaseUrl('/node_modules/cesium/Build/Cesium/');
export class MapCesium implements MapInterface {
private viewer: Cesium.Viewer | null = null;
private containerId = '';
init(container: HTMLElement, rectangle?: any): Promise<any> {
return new Promise(async (resolve, reject) => {
try {
this.containerId = container.id;
// ✅ 2. 初始化 Viewer
// 在 1.141.0 中,建议先不传 imageryProvider或者使用默认的 Ion Imagery
// 如果不想用 Ion可以传一个空的 ImageryProviderCollection 或者稍后移除
this.viewer = new Cesium.Viewer(this.containerId, {
animation: false, // 隐藏动画
timeline: false, // 隐藏时间线
baseLayerPicker: false, // 隐藏底图选择器
fullscreenButton: false, // 隐藏全屏按钮
vrButton: false, // 隐藏VR按钮
geocoder: false, // 隐藏地址搜索框
homeButton: false, // 隐藏首页按钮
sceneModePicker: false, // 隐藏场景模式选择器
navigationHelpButton: false, // 隐藏导航帮助按钮
infoBox: false, // 隐藏信息框
selectionIndicator: false, // 隐藏选择指示器
shouldAnimate: true // 开启动画
});
// // 清空默认图层
const layers = this.viewer.imageryLayers;
layers.removeAll();
// // 添加 ArcGIS 底图(正确 URL
const arcgisUrl =
'https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/';
const provider = await Cesium.ArcGisMapServerImageryProvider.fromUrl(
arcgisUrl,
{
enablePickFeatures: false,
// 可选:设置最大层级,避免请求过细的瓦片
maximumLevel: 19
}
);
// ✅ 6. 【关键】将 Provider 添加到图层集合中
layers.addImageryProvider(provider);
// layers.addImageryProvider(provider);
// // ✅ 5. 场景优化
this.viewer.scene.globe.depthTestAgainstTerrain = false;
// // 启用高 DPI 支持(可选,根据性能需求)
this.viewer.useBrowserRecommendedResolution = true;
// ✅ 6. 飞行到中国视角
// 使用 setTimeout 确保 DOM 和 WebGL 上下文完全就绪
// setTimeout(() => {
// this.flyToChina();
// resolve(this.viewer);
// }, 100);
} catch (error) {
console.error('Cesium Init Critical Error:', error);
reject(error);
}
});
}
private flyToChina() {
if (!this.viewer) return;
// 取消之前的飞行动画(如果有)
this.viewer.camera.cancelFlight();
this.viewer.camera.flyTo({
destination: Cesium.Cartesian3.fromDegrees(104.5, 36.5, 4000000),
orientation: {
heading: Cesium.Math.toRadians(0),
pitch: Cesium.Math.toRadians(-90),
roll: 0.0
},
duration: 2
});
}
destroy(): void {
if (this.viewer) {
// ✅ 关键:销毁前取消所有动画和事件
this.viewer.camera.cancelFlight();
this.viewer.destroy();
this.viewer = null;
}
}
flyTopanto(position: number[], zoom: number): void {
if (!this.viewer || !position) return;
const [lng, lat] = position;
const height = 20000000 / Math.pow(2, zoom);
this.viewer.camera.cancelFlight();
this.viewer.camera.flyTo({
destination: Cesium.Cartesian3.fromDegrees(
lng,
lat,
height > 100 ? height : 1000
),
duration: 1.5
});
}
// ... 其他空实现方法保持不变 ...
jdPanelControlShowAndHidden(baseid: string, isAll: boolean): void {}
mdLayerShowOrHidden(): void {}
addBaseDataLayer(layer: any): void {}
controlBaseLayerTreeShowAndHidden(
layerType: string,
key: string,
checked: boolean
): void {}
mdLayerTreeShowOrHidden(layerType: string, checked?: boolean): void {}
setLegendPointVisible(
layerKey: string,
anchoPointState: string,
checked: boolean
): void {}
addInitDataLayer(pointData: any[], layerType: any, mdoptions?: any): void {}
switchView(type: any): void {}
fitBounds(bounds: any): void {}
baseLayerSwitcher(key: string): void {}
addTertiarybasinLayer(
layer: layer,
fillcolor: any,
outlineColor: any,
datas: any
): void {}
hideTertiarybasinLayer(layer: layer): void {}
zoomToggle(type: 'out' | 'in'): void {
if (!this.viewer) return;
const factor = 1.5;
if (type === 'in') this.viewer.camera.zoomIn(factor);
else this.viewer.camera.zoomOut(factor);
}
lengthCalculate(): void {}
areCalculate(): void {}
removeQueryLayer(): void {}
// src/components/gis/map.cesium.ts
/**
*
* @param fileName 'cesium_map.png'
*/
mapOutPut(fileName = 'cesium_map.png'): void {
if (!this.viewer) {
console.warn('Viewer is not initialized.');
return;
}
try {
const canvas = this.viewer.canvas;
// ✅ 关键步骤 1: 强制渲染一帧,确保画布内容是最新的
// 这有助于解决因缓冲区清除导致的黑屏问题
this.viewer.render();
// ✅ 关键步骤 2: 尝试获取数据
// 注意:如果存在跨域污染,这里可能会抛出 SecurityError
const dataURL = canvas.toDataURL('image/png');
// 检查是否获取到了有效数据(简单的长度检查)
if (dataURL.length < 100) {
console.warn(
'Canvas data is too small, likely black or empty. Check CORS settings.'
);
// 可以尝试 fallback 到 blob 方式,但通常 CORS 问题两者都会失败
}
// 创建临时链接元素
const link = document.createElement('a');
link.href = dataURL;
link.download = fileName;
// 触发点击事件进行下载
document.body.appendChild(link);
link.click();
// 清理临时元素
document.body.removeChild(link);
console.log('Map image exported successfully:', fileName);
} catch (error) {
console.error('Failed to export map image:', error);
if (error instanceof DOMException && error.name === 'SecurityError') {
console.error(
'Security Error: The canvas has been tainted by cross-origin data. Ensure all imagery providers support CORS.'
);
}
}
}
// ... 其他代码 ...
initPopupOverlay(popupContainer: HTMLDivElement): void {}
}