实现效果:
实现步骤:
1、劫持cesium的瓦片销毁方法,实现资源回收
// 保存原始方法引用
const originalDestroyTile = Cesium.GlobeSurfaceTile.prototype.freeResources;
// 劫持瓦片销毁方法
Cesium.GlobeSurfaceTile.prototype.freeResources = function() {
// 触发自定义销毁逻辑
for (let imageInstance of this.imagery) {
let texture = [imageInstance.readyImagery,imageInstance.loadingImagery]
for (let tex of texture){
const provider = tex?.imageryLayer?.imageryProvider;
//查看源码referenceCount为0 才会立即释放
if (provider?.onTileDestroy && tex.referenceCount == 1) {
provider.onTileDestroy(
tex.x,
tex.y,
tex.level
);
}
}
}
// 调用原始销毁方法
originalDestroyTile.call(this);
};
2、劫持瓦片加载方法,实现瓦片展示
// 保存原始方法引用
const originalTileProvider = Cesium.GlobeSurfaceTileProvider.prototype.endUpdate;
Cesium.GlobeSurfaceTileProvider.prototype.endUpdate = function (frameState) {
const tilesToRenderByTextureCount = this._tilesToRenderByTextureCount;
for (const imageLayer of this._imageryLayers._layers){
const provider = imageLayer.imageryProvider;
if (provider?.clearAll){
provider.clearAll();
}
}
for (
let textureCountIndex = 0,
textureCountLength = tilesToRenderByTextureCount.length;
textureCountIndex < textureCountLength;
++textureCountIndex
) {
const tilesToRender = tilesToRenderByTextureCount[textureCountIndex];
if (!tilesToRender) {
continue;
}
for (
let tileIndex = 0, tileLength = tilesToRender.length;
tileIndex < tileLength;
++tileIndex
) {
const tile = tilesToRender[tileIndex];
const surfaceTile = tile.data;
const tileImageryCollection = surfaceTile.imagery;
let imageryIndex = 0;
const imageryLen = tileImageryCollection.length;
while (imageryIndex < imageryLen) {
const tileImagery = tileImageryCollection[imageryIndex];
const imagery = tileImagery.readyImagery;
++imageryIndex;
if (!imagery){
continue;
}
const provider = imagery?.imageryLayer?.imageryProvider;
if (provider?.onTileLoad) {
provider.onTileLoad(
imagery.x,
imagery.y,
imagery.level
);
}
}
}
}
originalTileProvider.call(this, frameState);
}
3、自定义瓦片provider,实现瓦片转groundPrimitive
requestImage(x, y, level) {
console.log(`Loading tile x:${x}, y:${y}, level:${level}`);
//替换URL模板参数
if (level < this.minZoom || level > this.maxZoom){
return new Promise((resolve, reject) => {
resolve(null);
});
}
let url;
if (this._wmsParams) {
const bbox = this._calculateWMSBBox(x, y, level);
url = this._buildWMSUrl(x, y, level, bbox);
} else {
url = this._buildStandardUrl(x, y, level);
}
let _this = this;
return new Promise((resolve, reject) => {
const image = new Image();
image.crossOrigin = "anonymous"; // 处理跨域请求
image.onload = () => {
// 创建原始几何体并存储引用
_this._createGroundPrimitive(x, y, level, image);
resolve(image);
};
image.onerror = (e) => {
//console.error(`Failed to load tile: ${url}`, e);
reject(new Cesium.TileProviderError("Failed to load image", undefined, x, y, level));
};
image.src = url;
});
}
4、使用方法
//html中引用js文件
<script src="/resource/scripts/GroundImageryProvider.js"></script>
//添加代码
//wms图层
wmsImageryProvider = GroundImageryProvider.fromUrl(url+ 'geoserver/penghui/wms',{
wmsParams: {
transparent: true,
format: 'image/png',
srs: 'EPSG:4326',
styles: '',
CQL_FILTER: cqlfilter,
layers: 'xxx',
},
viewer: viewer
})
viewer.imageryLayers.addImageryProvider(wmsImageryProvider );
//瓦片
provider = GroundImageryProvider.fromUrl(url, {viewer: viewer});
viewer.imageryLayers.addImageryProvider(provider);
完整代码:
// 保存原始方法引用
const originalDestroyTile = Cesium.GlobeSurfaceTile.prototype.freeResources;
// 劫持瓦片销毁方法
Cesium.GlobeSurfaceTile.prototype.freeResources = function() {
// 触发自定义销毁逻辑
for (let imageInstance of this.imagery) {
let texture = [imageInstance.readyImagery,imageInstance.loadingImagery]
for (let tex of texture){
const provider = tex?.imageryLayer?.imageryProvider;
//查看源码referenceCount为0 才会立即释放
if (provider?.onTileDestroy && tex.referenceCount == 1) {
provider.onTileDestroy(
tex.x,
tex.y,
tex.level
);
}
}
}
// 调用原始销毁方法
originalDestroyTile.call(this);
};
// 保存原始方法引用
const originalTileProvider = Cesium.GlobeSurfaceTileProvider.prototype.endUpdate;
Cesium.GlobeSurfaceTileProvider.prototype.endUpdate = function (frameState) {
const tilesToRenderByTextureCount = this._tilesToRenderByTextureCount;
for (const imageLayer of this._imageryLayers._layers){
const provider = imageLayer.imageryProvider;
if (provider?.clearAll){
provider.clearAll();
}
}
for (
let textureCountIndex = 0,
textureCountLength = tilesToRenderByTextureCount.length;
textureCountIndex < textureCountLength;
++textureCountIndex
) {
const tilesToRender = tilesToRenderByTextureCount[textureCountIndex];
if (!tilesToRender) {
continue;
}
for (
let tileIndex = 0, tileLength = tilesToRender.length;
tileIndex < tileLength;
++tileIndex
) {
const tile = tilesToRender[tileIndex];
const surfaceTile = tile.data;
const tileImageryCollection = surfaceTile.imagery;
let imageryIndex = 0;
const imageryLen = tileImageryCollection.length;
while (imageryIndex < imageryLen) {
const tileImagery = tileImageryCollection[imageryIndex];
const imagery = tileImagery.readyImagery;
++imageryIndex;
if (!imagery){
continue;
}
const provider = imagery?.imageryLayer?.imageryProvider;
if (provider?.onTileLoad) {
provider.onTileLoad(
imagery.x,
imagery.y,
imagery.level
);
}
}
}
}
originalTileProvider.call(this, frameState);
}
class GroundImageryProvider {
constructor(url, options = {}) {
console.log('Initializing GroundImageryProvider');
// 基础配置
this.tilingScheme = this._tilingScheme = options.tilingScheme || new Cesium.WebMercatorTilingScheme();
this.rectangle = this._rectangle = this._tilingScheme.rectangle;
this.tileSize = this._tileSize = options.tileSize || 256;
this.tileWidth = this._tileWidth = this._tileSize;
this.tileHeight = this._tileHeight = this._tileSize;
this.maxZoom = this._maximumLevel = options.maximumLevel || 27;
this.minZoom = options.minimumLevel || 13;
this._height = options.height || 0;
this.hasAlphaChannel = true;
this._viewer = options.viewer;
this._wmsParams = options.wmsParams;
this._rendered = false;
// 状态标识
this.ready = this._ready = true;
this.destroyed = this._destroyed = false;
this._url = url;
this._tilePrimitives = new Map();
// 直接标记为就绪
this.readyPromise = this._readyPromise = Promise.resolve(true);
}
static fromUrl(url, options) {
const provider = new GroundImageryProvider(url, options);
return provider;
}
// 新增WMS BBOX计算
_calculateWMSBBox(x, y, level) {
const tilingScheme = this._tilingScheme;
const rectangle = tilingScheme.tileXYToRectangle(x, y, level);
// 根据CRS版本处理坐标顺序
if (this._wmsParams.version === '1.3.0' && this._wmsParams.crs === 'EPSG:4326') {
return `${rectangle.south * 180 / Math.PI},${rectangle.west * 180 / Math.PI},` +
`${rectangle.north * 180 / Math.PI},${rectangle.east * 180 / Math.PI}`;
} else {
return `${rectangle.west * 180 / Math.PI},${rectangle.south * 180 / Math.PI},` +
`${rectangle.east * 180 / Math.PI},${rectangle.north * 180 / Math.PI}`;
}
}
// 构建WMS请求URL
_buildWMSUrl(x, y, level, bbox) {
const params = {
service: 'WMS',
request: 'GetMap',
width: this._tileSize,
height: this._tileSize,
bbox: bbox,
...this._wmsParams
};
const query = Object.entries(params)
.map(([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(v)}`)
.join('&');
return `${this._url}?${query}`;
}
// 标准URL构建(原有逻辑)
_buildStandardUrl(x, y, level) {
return this._url
.replace('{x}', x)
.replace('{y}', y)
.replace('{z}', level);
}
requestImage(x, y, level) {
console.log(`Loading tile x:${x}, y:${y}, level:${level}`);
//替换URL模板参数
if (level < this.minZoom || level > this.maxZoom){
return new Promise((resolve, reject) => {
resolve(null);
});
}
let url;
if (this._wmsParams) {
const bbox = this._calculateWMSBBox(x, y, level);
url = this._buildWMSUrl(x, y, level, bbox);
} else {
url = this._buildStandardUrl(x, y, level);
}
let _this = this;
return new Promise((resolve, reject) => {
const image = new Image();
image.crossOrigin = "anonymous"; // 处理跨域请求
image.onload = () => {
// 创建原始几何体并存储引用
_this._createGroundPrimitive(x, y, level, image);
resolve(image);
};
image.onerror = (e) => {
//console.error(`Failed to load tile: ${url}`, e);
reject(new Cesium.TileProviderError("Failed to load image", undefined, x, y, level));
};
image.src = url;
});
}
onTileDestroy(x, y, level) {
const key = `${level}/${x}/${y}`;
const primitive = this._tilePrimitives.get(key);
if (primitive) {
//to do 网络请求取消,提升性能
console.log(`Destroy tile ${key}`);
this.destroyPrimitive(primitive);
this._tilePrimitives.delete(key);
}
}
onTileLoad(x, y, level) {
const key = `${level}/${x}/${y}`;
const primitive = this._tilePrimitives.get(key);
if (primitive) {
//to do 网络请求取消,提升性能
primitive.show = true;
}else{
console.log(key +"---error");
}
}
clearAll(){
this._tilePrimitives.forEach((primitive) => {
primitive.show = false;
});
}
destroyPrimitive(primitive) {
const scene = this._viewer.scene;
if (!scene || !primitive) return;
// 分阶段销毁
try {
// 1. 移除原始对象
scene.primitives.remove(primitive);
} catch (e) {
console.warn('Primitive销毁异常:', e);
}
}
_createGroundPrimitive(x, y, level, image) {
const scene = this._viewer.scene;
if (!scene) {
console.error('No active scene found');
return null;
}
const key = `${level}/${x}/${y}`;
if (this._tilePrimitives.get(key)){
return;
}
const primitive = new Cesium.GroundPrimitive({
geometryInstances: new Cesium.GeometryInstance({
geometry: new Cesium.RectangleGeometry({
rectangle: Cesium.Rectangle.fromDegrees(
x * 1.0 / Math.pow(2, level) * 360 - 180,
Math.atan(Math.sinh(Math.PI * (1 - 2 * (y + 1) / Math.pow(2, level)))) * 180 / Math.PI,
(x + 1) * 1.0 / Math.pow(2, level) * 360 - 180,
Math.atan(Math.sinh(Math.PI * (1 - 2 * y / Math.pow(2, level)))) * 180 / Math.PI,
),
height: 0.0
})
}),
appearance: new Cesium.MaterialAppearance({ // 改用 MaterialAppearance
material: new Cesium.Material({
fabric: {
type: 'Image',
uniforms: {
image: image, // 使用传入的图片资源
//depthOffsetFactor: 1.0
}
},
translucent: true // 根据图片透明度需要设置
}),
flat: true, // 保持平面着色
closed: true, // 是否闭合几何体
}),
asynchronous: false,
classificationType: Cesium.ClassificationType.BOTH,
// 重要参数:启用释放控制
releaseGeometryInstances: true, // 允许释放几何实例
allowPicking: false, // 禁用拾取减少引用
_zIndex: Math.ceil(this._height * 10),
});
scene.primitives.add(primitive);
this._tilePrimitives.set(key, primitive);
return primitive;
}
pickFeatures(x, y, level, longitude, latitude) {
console.log(`Pick features at x:${x}, y:${y}, level:${level}`);
const key = `${level}/${x}/${y}`;
const primitive = this._tilePrimitives.get(key)
return undefined; // 禁用要素拾取
}
destroy() {
console.log('Destroying provider');
// 清理所有原始几何体
this._tilePrimitives.forEach((primitive) => {
this.destroyPrimitive(primitive);
});
this._tilePrimitives.clear();
this._destroyed = true;
}
get isDestroyed() {
return this._destroyed;
}
}