cesium 实现瓦片地图贴模型、贴地

实现效果:

实现步骤:

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;
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值