i recently made it as to add VectorTileServer layer in a cesium map.
hope this could be helpful for anyone.
<html>
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta
name="viewport"
content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no"
/>
<title>Cesium + Webpack</title>
<style>
body,
#cesiumContainer {
width: 100%;
height: 100%;
margin: 0;
}
.toolbar-item {
display: inline-block;
margin-right: 10px;
}
</style>
<link
href="https://cesium.com/downloads/cesiumjs/releases/1.111/Build/Cesium/Widgets/widgets.css"
rel="stylesheet"
/>
<script src="https://cesium.com/downloads/cesiumjs/releases/1.111/Build/Cesium/Cesium.js"></script>
<script src="https://github.com/landtechnologies/Mapbox-vector-tiles-basic-js-renderer/blob/master/dist/mapbox-gl.js"></script> -->
<link href="https://github.com/landtechnologies/Mapbox-vector-tiles-basic-js-renderer/blob/master/dist/mapbox-gl.css" rel="stylesheet" />
<script>
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._originStyle = JSON.parse(JSON.stringify(options.style));
this.ready = false;
this.readyPromise = this.mapboxRenderer._style.loadedPromise.then(
() => (this.ready = true)
);
this.tilingScheme = [4490, 4326].includes(options.wkid)
? new Cesium.GeographicTilingScheme()
: 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) {
zoom += 1;
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) {
console.log(canv.toDataURL( 'image/png' ));
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 + 1,
lng: longitude,
lat: latitude,
tileZ: zoom + 1,
}),
});
});
// release tile
renderRef.consumer.ctx = undefined;
this.mapboxRenderer.releaseRender(renderRef);
return queryResult;
});
}
destroy() {
this.resetTile();
this.mapboxRenderer._gl
.getExtension("WEBGL_lose_context")
.loseContext();
}
resetTile() {
this.mapboxRenderer._cancelAllPendingRenders();
Object.values(this.mapboxRenderer._style.sourceCaches).forEach(
(cache) => {
cache._tileCache.reset();
cache._tileCache.remove();
}
);
}
setOnlyVisible(visible = true, layerId, field, fieldValue) {
this.resetTile();
const style = this.mapboxRenderer._style;
const _order = style._order;
style.setLayers(_order);
_order.forEach(l=>style.setFilter(l));
if (layerId || field) {
this.mapboxRenderer._style.setLayers(
visible ? [layerId] : _order.filter((l) => l !== layerId)
);
if(field){
this.mapboxRenderer.setFilter(layerId,[visible?'==':'!=',field, fieldValue]);
}
}
}
}
async function getGeosceneVecTile(arcVecUrl, isCache = false) {
return Promise.all([
fetch(arcVecUrl + "?f=json"),
fetch(arcVecUrl + "/resources/styles"),
])
.then(([r1, r2]) => Promise.all([r1.json(), r2.json()]))
.then(([info, style]) => {
const { wkid, latestWkid } = info.tileInfo.spatialReference;
style.sprite = arcVecUrl + "/resources/sprites/sprite";
style.glyphs =
arcVecUrl + "/resources/fonts/{fontstack}/{range}.pbf";
delete style.sources.esri.url;
style.sources.esri.tiles = [`${arcVecUrl}/tile/{z}/{y}/{x}.pbf`];
const provider = new MVTImageryProvider({
style,
wkid: wkid || latestWkid,
isCache,
});
return provider.readyPromise.then(() => ({
provider,
layerIds: style.layers.map((l) => l.id),
}));
});
}
</script>
</head>
<body>
<div id="cesiumContainer">loading...</div>
<div
id="cesiumToolBarDom"
style="
position: absolute;
top: 10px;
left: 10px;
max-height: 200px;
overflow-y: scroll;
width: 30%;
background: white;
font-size: 12px;
"
></div>
<script>
const arcgisVecUrl = "https://basemaps.arcgis.com/arcgis/rest/services/World_Basemap_v2/VectorTileServer"
window.onload = function () {
// TODO: key code
window.Mapbox = window["mapbox-gl"];
window.cesiumContainer.innerText = "";
Cesium.Ion.defaultServer = "";
const cesiumViewer = new Cesium.Viewer("cesiumContainer", {
infoBox: false,
baseLayerPicker: false,
terrainProvider: undefined,
imageryProvider: new Cesium.GridImageryProvider(),
});
window.cesiumViewer = cesiumViewer;
const RADIO_CLASS='RADIO_CLASS';
getGeosceneVecTile(
arcgisVecUrl
).then(({ provider, layerIds }) => {
window.cesiumToolBarDom.innerHTML = "";
let layer = cesiumViewer.imageryLayers.addImageryProvider(provider);
const contents = layerIds
.map(
(i) =>
`<div class="toolbar-item"><input class="${RADIO_CLASS}" type="radio" value="${i}">${i}</div>`
)
.join("");
window.cesiumToolBarDom.innerHTML = `<div class="toolbar-item"><input type="radio" value="" class="${RADIO_CLASS}" checked >全部</div>${contents}`;
window.cesiumToolBarDom.onclick = ({ target }) => {
if (target?.nodeName === "INPUT" && target?.className ===RADIO_CLASS) {
cesiumViewer.imageryLayers.remove(layer,true);
Array.from(
window.cesiumToolBarDom.getElementsByTagName("input")
).forEach((pt) => {
pt.checked = pt === target ? true : false;
});
provider.setOnlyVisible(true, target.value);
layer = cesiumViewer.imageryLayers.addImageryProvider(provider);
}
};
});
};
</script>
</body>
</html>
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.