//按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,
|
transformRequest: (url) => this.transformRequest(url),
|
});
|
|
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.headers) {
|
return {
|
url,
|
headers: this.options.headers,
|
};
|
}
|
return {
|
url,
|
};
|
};
|
|
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);
|