lg_frontend/static/libs/mars3d/thirdParty/pbf-mapbox/PbfLayer.js

245 lines
8.3 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// import * as mars3d from "mars3d"
// const Cesium = mars3d.Cesium
// 按mars3d规范封装的pbf图层
;(function (window) {
const 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 {
/**
*
* @param {Object} options
* @param {Object} options.style - mapbox style object
* @param {Function} [options.sourceFilter] - sourceFilter is used to filter which source participate in pickFeature process.
* @param {Number} [options.maximumLevel] - if cesium zoom level exceeds maximumLevel, layer will be invisible.
* @param {Number} [options.minimumLevel] - if cesium zoom level belows minimumLevel, layer will be invisible.
* @param {Number} [options.tileSize=512] - can be 256 or 512.
* @param {Boolean} [options.hasAlphaChannel] -
* @param {String} [options.credit] -
*
*/
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)
}
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 || 512
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: "" }
}
getTileCredits(x, y, level) {
return []
}
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
}
_getTilesSpec(coord, source) {
const { x, y, zoom } = coord
const TILE_SIZE = this.tileSize
// 3x3 grid of source tiles, where the region of interest is that corresponding to the central source tile
const ret = []
const maxTile = (1 << zoom) - 1
for (let xx = -1; xx <= 1; xx++) {
let newx = x + xx
if (newx < 0) {
newx = maxTile
}
if (newx > maxTile) {
newx = 0
}
for (let yy = -1; yy <= 1; yy++) {
const newy = y + yy
if (newy < 0) {
continue
}
if (newy > maxTile) {
continue
}
ret.push({
source: source,
z: zoom,
x: newx,
y: newy,
left: 0 + xx * TILE_SIZE,
top: 0 + yy * TILE_SIZE,
size: TILE_SIZE
})
}
}
return ret
}
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().reduce((a, s) => a.concat(this._getTilesSpec({ x, y, zoom }, s)), [])
return new Promise((resolve, reject) => {
const canv = this.createTile()
const ctx = canv.getContext("2d")
const renderRef = this.mapboxRenderer.renderTiles(
ctx,
{
srcLeft: 0,
srcTop: 0,
width: this.tileSize,
height: this.tileSize,
destLeft: 0,
destTop: 0
},
tilesSpec,
(err) => {
/**
* In case of err ends with 'tiles not available', the canvas will still be painted.
* relate url: https://github.com/landtechnologies/Mapbox-vector-tiles-basic-js-renderer/blob/master/src/basic/renderer.js#L341-L405
*/
if (typeof err === "string" && !err.endsWith("tiles not available")) {
reject(undefined)
} else if (releaseTile) {
renderRef.consumer.ctx = undefined
resolve(canv)
// 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 = []
longitude = Cesium.Math.toDegrees(longitude)
latitude = Cesium.Math.toDegrees(latitude)
targetSources.forEach((s) => {
const data = this.mapboxRenderer.queryRenderedFeatures({
source: s,
renderedZoom: zoom,
lng: longitude,
lat: latitude,
tileZ: zoom
})
for (const key in data) {
const item = data[key]
for (let index = 0; index < item.length; index++) {
const element = item[index]
element.layer = key
queryResult.push({ properties: element })
}
}
})
// release tile
renderRef.consumer.ctx = undefined
this.mapboxRenderer.releaseRender(renderRef)
this.mapboxRenderer._style.sourceCaches?.origin?._tileCache.reset()
return queryResult
})
}
destroy() {
this.mapboxRenderer._cancelAllPendingRenders()
Object.values(this.mapboxRenderer._style.sourceCaches).forEach((cache) => cache._tileCache.reset())
this.mapboxRenderer._gl.getExtension("WEBGL_lose_context").loseContext()
}
}
class PbfLayer extends mars3d.layer.BaseTileLayer {
// 构建ImageryProvider
async _createImageryProvider(options) {
return createImageryProvider(options)
}
}
async function createImageryProvider(options) {
if (options.url) {
let data = await Cesium.Resource.fetchJson(options)
const provider = new MVTImageryProvider({ ...options, style: data })
await provider.readyPromise
return provider
} else {
const provider = new MVTImageryProvider(options)
await provider.readyPromise
return provider
}
}
PbfLayer.createImageryProvider = createImageryProvider
// 注册下
const layerType = "pbf" // 图层类型
mars3d.LayerUtil.register(layerType, PbfLayer)
mars3d.LayerUtil.registerImageryProvider(layerType, createImageryProvider)
// 对外接口
window.mars3d.layer.PbfLayer = PbfLayer
})(window)
// export { PbfLayer }