提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
目录
前言
一、为什么使用mapbox_gl这种显示方式?
二、使用步骤
1.引入库
2.调用
3 核心的扩展类
4.全部代码
5 效果
总结
Cesium并不支持矢量瓦片数据格式,通过询问官方,得到的结论是,他们并没有计划增加这个功能,最近的项目是需要将城市建筑物显示在Cesium中,如果将其转为3Dtiles格式,需要自己写代码转格式,并且数据量大的情况下,需要占用大量的存储空间,所以研究Cesium地图加载矢量瓦片。
网上有两种显示方式,一种是采用geoserver+ol的方式,另外一种是通过mapbox_gl显示矢量瓦片的方法渲染,据说这种方式要比geoserver+ol的方式迅速,经过了一系列的实践,终于达到了显示目的,但最终目的还需要进一步探索。
代码如下:
代码如下(示例):
fetch("https://api.maptiler.com/maps/basic/style.json?key=pSHUA9sSkNny3iqoWG4P")
.then((res) => res.json())
.then((style) => {
const provider = new MVTImageryProvider({
style,
});
provider.readyPromise.then(() => {
cesiumViewer.imageryLayers.addImageryProvider(provider);
});
});
该处使用的fetch的方式请求的网络数据。
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.mapboxRenderer = new Mapbox.BasicRenderer({ style: options.style });
this.ready = false;
this.readyPromise = this.mapboxRenderer._style.loadedPromise.then(
() => (this.ready = true)
);
this.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 !== undefined ? options.hasAlphaChannel : true;
this.sourceFilter = options.sourceFilter;
}
getTileCredits(x, y, level) {
return [];
}
createTile() {
let canv = document.createElement("canvas");
canv.width = this.tileSize;
canv.height = this.tileSize;
canv.style.imageRendering = "pixelated";
canv.getContext("2d").globalCompositeOperation = "copy";
return canv;
}
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().forEach((s) => {
tilesSpec.push({
source: s,
z: zoom,
x: x,
y: y,
left: 0,
top: 0,
size: this.tileSize,
});
});
return new Promise((resolve, reject) => {
let 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,
(err) => {
if (!!err) {
switch (err) {
case "canceled":
case "fully-canceled":
reject(undefined);
break;
default:
reject(undefined);
}
} else {
if (releaseTile) {
renderRef.consumer.ctx = undefined;
resolve(canv);
// releaseTile默认为true,对应Cesium请求图像的情形
this.mapboxRenderer.releaseRender(renderRef);
} 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();
targetSources = this.sourceFilter
? this.sourceFilter(targetSources)
: targetSources;
const queryResult = [];
longitude = Cesium.Math.toDegrees(longitude);
latitude = Cesium.Math.toDegrees(latitude);
targetSources.forEach((s) => {
queryResult.push({
data: this.mapboxRenderer.queryRenderedFeatures({
source: s,
renderedZoom: zoom,
lng: longitude,
lat: latitude,
tileZ: zoom,
}),
});
});
// release tile
renderRef.consumer.ctx = undefined;
this.mapboxRenderer.releaseRender(renderRef);
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();
}
}
Cesium + Webpack
具体过程,大概就是通过一个依赖于mapbox_gl这样一个开源的库实现的绘制渲染,实际加载的pbf格式的矢量瓦片。
这里需要注意的一点是,我是从html页面直接加载的,如果需要使用es6 webpak的方式加载,就需要注意模块引入的方式,这里是使用CommonJS的模块加载方式的,他是不能和es6混用的,否则会报错的,导致无法加载。
其次,如果是自己Geoserver的数据,需要按照指定的配置文件格式
//测试本地geoserver
const geoserverStyle = {
version: 8,
sources: {
countries: {
type: "vector",
'scheme': 'tms',
tiles: ['http://localhost:8080/geoserver/gwc/service/tms/1.0.0/topp%3Astates@EPSG%3A900913@pbf/{z}/{x}/{y}.pbf'],
maxzoom: 6,
},
},
glyphs: "https://cdn.rawgit.com/klokantech/mapbox-gl-js-offline-example/v1.0/font/{fontstack}/{range}.pbf",
layers: [
{
id: "buildinds_ly",
'source': 'countries',
'source-layer': 'states',
type: "fill",
paint: {
"fill-color": "#fdaf6b",
},
}
]
};