//按mars3d规范,封装的pbf图层 ;(function (window) { let BasicRenderer = window.mapboxRenderer.BasicRenderer // 创建一个全局变量作为pbfBasicRenderer渲染模板,避免出现16个canvas上下文的浏览器限制,以便Cesium ImageLayer.destory()正常工作。 // https://github.com/mapbox/mapbox-gl-js/issues/7332 const OFFSCREEN_CANV_SIZE = 1024 const baseCanv = document.createElement("canvas") baseCanv.style.imageRendering = "pixelated" baseCanv.addEventListener("webglcontextlost", () => console.log("webglcontextlost"), false) baseCanv.width = OFFSCREEN_CANV_SIZE baseCanv.height = OFFSCREEN_CANV_SIZE class MVTImageryProvider { /** * create a MVTImageryProvider Object * @param options MVTImageryProvider options * @param options.style - mapbox style object * @param options.sourceFilter - sourceFilter is used to filter which source participate in pickFeature process. * @param options.maximumLevel - if cesium zoom level exceeds maximumLevel, layer will be invisible. * @param options.minimumLevel - if cesium zoom level belows minimumLevel, layer will be invisible. * @param options.tileSize - can be 256 or 512. 256 default * @param options.headers - url fetch request headers * @param options.tilingScheme - Cesium tilingScheme, default WebMercatorTilingScheme(EPSG: 3857) */ constructor(options) { this.options = options let pbfStyle = options.style this.mapboxRenderer = new BasicRenderer({ style: pbfStyle }) this.mapboxRenderer._transformRequest = (url, resourceType) => { return this.transformRequest(url, resourceType) } console.log( this.mapboxRenderer) this.mapboxRenderer._canvas = baseCanv this.mapboxRenderer._canvas.addEventListener("webglcontextrestored", () => this.mapboxRenderer._createGlContext(), false) this.mapboxRenderer._createGlContext() if (options.showCanvas) { this.mapboxRenderer.showCanvasForDebug() } this.ready = false this.readyPromise = this.mapboxRenderer._style.loadedPromise.then(() => { this.ready = true }) this.tilingScheme = options.tilingScheme ?? new Cesium.WebMercatorTilingScheme() this.rectangle = this.tilingScheme.rectangle this.tileSize = this.tileWidth = this.tileHeight = options.tileSize || 1024 this.maximumLevel = options.maximumLevel || Number.MAX_SAFE_INTEGER this.minimumLevel = options.minimumLevel || 0 this.tileDiscardPolicy = undefined //this.errorEvent = new Cesium.Event(); this.credit = new Cesium.Credit(options.credit || "", false) this.proxy = new Cesium.DefaultProxy("") this.hasAlphaChannel = options.hasAlphaChannel ?? true this.sourceFilter = options.sourceFilter } transformRequest = (url) => { if (this.options.transformUrl) { url = this.options.transformUrl(url) } return { url: url, headers: this.options.headers || {}, credentials: "" } } createTile() { const canv = document.createElement("canvas") canv.width = this.tileSize canv.height = this.tileSize canv.style.imageRendering = "pixelated" const ctx = canv.getContext("2d") if (ctx) { ctx.globalCompositeOperation = "copy" } return canv } async canvasToImage(canvas) { const img = new Image() return new Promise((resolve) => { img.onload = function () { resolve(img) } img.crossOrigin = "" img.src = canvas.toDataURL("image/png") }) } requestImage(x, y, zoom, releaseTile = true) { if (zoom > this.maximumLevel || zoom < this.minimumLevel) { return Promise.reject(undefined) } this.mapboxRenderer.filterForZoom(zoom) const tilesSpec = [] this.mapboxRenderer.getVisibleSources(zoom).forEach((s) => { tilesSpec.push({ source: s, z: zoom, x: x, y: y, left: 0, top: 0, size: this.tileSize }) }) return new Promise((resolve, reject) => { const canv = this.createTile() const renderRef = this.mapboxRenderer.renderTiles( canv.getContext("2d"), { srcLeft: 0, srcTop: 0, width: this.tileSize, height: this.tileSize, destLeft: 0, destTop: 0 }, tilesSpec, async (err) => { if (!!err) { switch (err) { case "canceled": case "fully-canceled": reject(undefined) break default: reject(undefined) } } else { if (releaseTile) { renderRef.consumer.ctx = null const img = await this.canvasToImage(canv) resolve(img) // releaseTile默认为true,对应Cesium请求图像的情形 this.mapboxRenderer.releaseRender(renderRef) this.mapboxRenderer._style.sourceCaches?.origin?._tileCache.reset() } else { // releaseTile为false时在由pickFeature手动调用,在渲染完成之后在pickFeature里边手动释放tile resolve(renderRef) } } } ) }) } pickFeatures(x, y, zoom, longitude, latitude) { return this.requestImage(x, y, zoom, false)?.then((renderRef) => { let targetSources = this.mapboxRenderer.getVisibleSources(zoom) targetSources = this.sourceFilter ? this.sourceFilter(targetSources) : targetSources const queryResult = [] const lng = Cesium.Math.toDegrees(longitude) const lat = Cesium.Math.toDegrees(latitude) targetSources.forEach((s) => { queryResult.push({ data: this.mapboxRenderer.queryRenderedFeatures({ source: s, renderedZoom: zoom, lng, lat, tileZ: zoom }) }) }) console.log(queryResult) // release tile renderRef.consumer.ctx = undefined this.mapboxRenderer.releaseRender(renderRef) this.mapboxRenderer._style.sourceCaches?.origin?._tileCache.reset() return queryResult }) } } class PbfLayer extends mars3d.layer.BaseTileLayer { //构建ImageryProvider _createImageryProvider(options) { return createImageryProvider(options) } } function createImageryProvider(options) { if (options.url) { return Cesium.Resource.fetchJson(options).then((data) => { return new MVTImageryProvider({ ...options, style: data }) }) } else { return new MVTImageryProvider(options) } } PbfLayer.createImageryProvider = createImageryProvider //注册下 const layerType = "pbf" //图层类型 mars3d.LayerUtil.register(layerType, PbfLayer) mars3d.LayerUtil.registerImageryProvider(layerType, createImageryProvider) //对外接口 window.mars3d.layer.PbfLayer = PbfLayer })(window)