集成Three.js
Three.js是基于原生WebGL封裝運(yùn)行的三維引擎庫(kù),在所有WebGL引擎中,Three.js是國(guó)內(nèi)文資料最多、使用最廣泛的三維引擎。Three.js可應(yīng)用于Web 3D的可視化(如產(chǎn)品在線瀏覽、在線三維可視化等),H5/微信小程序游戲(如跳一跳),科教領(lǐng)域,機(jī)械領(lǐng)域,WebVR(VR看房、VR看車等)以及家裝室內(nèi)設(shè)計(jì)等方面,是一個(gè)比較輕量級(jí)的跨瀏覽器JavaScript庫(kù) ,適合在瀏覽器中創(chuàng)建和顯示動(dòng)畫3D計(jì)算機(jī)圖形。將Cesium的行星級(jí)渲染和GIS功能與Three.js廣泛而易用的通用3D API相結(jié)合,為新的WebGL體驗(yàn)開啟了許多可能性。兩者的集成總體思路如下:
(1)創(chuàng)建兩個(gè)容器,分別用于顯示cesium和three的場(chǎng)景
(2)初始化cesium、three渲染器
(3)調(diào)整three和cesium的渲染頻率保持一致
(4)調(diào)整three和cesium的相機(jī)位置角度保持一致
(5)加入要展示的圖形
以下展示了部分核心代碼。
<div id="cesiumContainer"></div>
<div id="ThreeContainer"></div>
// 2-1.初始化cesium
// cesium初始化時(shí),要將它的自動(dòng)渲染關(guān)掉(即useDefaultRenderLoop屬性調(diào)整為false)
cesium.viewer = new Cesium.Viewer("cesiumContainer", {
useDefaultRenderLoop: false, // 關(guān)閉自動(dòng)渲染
...
});
// 2-2.初始化three
function initThree() {
let fov = 45;
let width = window.innerWidth;
let height = window.innerHeight;
let aspect = width / height;
let near = 1;
let far = 10 * 1000 * 1000; // needs to be far to support Cesium's world-scale rendering
three.scene = new THREE.Scene();
three.camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
three.renderer = new THREE.WebGLRenderer({ alpha: true }); ThreeContainer.appendChild(three.renderer.domElement);
}
// 3.調(diào)整three和cesium的渲染頻率
// 手動(dòng)開啟cesium和three的渲染,并放進(jìn)一個(gè)渲染頻率里。
function loop() {
requestAnimationFrame(loop);
renderCesium();
renderThreeObj();
// 4.調(diào)整相機(jī)一致
// 這里使用的cesium的相機(jī)為主相機(jī),使three的相機(jī)與cesium保持一致即可。
renderCamera();
}
// 5.加入要展示的圖形
// 這里加入一個(gè)cesium的圖形polygon,再加入一個(gè)three的球體,以及一個(gè)three的12面體。
function init3DObject() {
let entity = {
name: "Polygon",
polygon: {
hierarchy: Cesium.Cartesian3.fromDegreesArray([
minWGS84[0],
minWGS84[1],
maxWGS84[0],
minWGS84[1],
maxWGS84[0],
maxWGS84[1],
minWGS84[0],
maxWGS84[1],
]),
material: Cesium.Color.RED.withAlpha(0.1),
},
};
let Polypon = cesium.viewer.entities.add(entity);
let doubleSideMaterial = new THREE.MeshNormalMaterial({
side: THREE.DoubleSide,
});
geometry = new THREE.SphereGeometry(1, 32, 32);
let sphere = new THREE.Mesh(
geometry,
new THREE.MeshPhongMaterial({
color: 0xffffff,
side: THREE.DoubleSide,
})
); //12面體
...
}
}
效果圖如下:

集成Echarts
Echarts 是一個(gè)基于 JavaScript 的開源可視化圖表庫(kù),具有豐富的圖表類型,可用于地理數(shù)據(jù)可視化的地圖、熱力圖、線圖等。Cesium通過與Echarts的地理數(shù)據(jù)可視化能力相結(jié)合,大大增強(qiáng)Cesium整體的可視化效果。本文通過封裝EchartsLayer來實(shí)現(xiàn)遷徙圖的效果。需要注意的是,在圖表的option配置項(xiàng)中不需要寫geo,同時(shí)每個(gè)series數(shù)組中元素都必須加coordinateSystem:'GLMap'。部分核心代碼如下:
var EchartsLayer = function (map, options) {
this._map = map;
this._overlay = this._createChartOverlay();
if (options) {
this._registerMap();
}
this._overlay.setOption(options || {});
};
let _echartLayer = new EchartsLayer(viewer, option);
簡(jiǎn)單效果圖如下所示:

集成heatmap
heatmap.js是一個(gè)輕量級(jí)的、最先進(jìn)的用于表達(dá)熱力圖的可視化前端庫(kù),比如人群分布情況、污染物濃度變化情況、信號(hào)強(qiáng)度等。感興趣的同學(xué)可以進(jìn)入官網(wǎng)https://www.patrick-wied.at/static/heatmapjs/ 查看詳情。
下面說一下cesium和heatmap集成的原理,其實(shí)也很簡(jiǎn)單,就是把使用heatmap.js生成的熱力圖,以貼圖材質(zhì)的方式賦給某個(gè)幾何圖形貼圖屬性。部分核心代碼如下:
// 根據(jù)熱力圖圖片范圍,生成隨機(jī)熱力點(diǎn)和強(qiáng)度值
var dataRaw = [];
for (var i = 0; i < len; i++) {
var point = {
lat: latMin + Math.random() * (latMax - latMin),
lon: lonMin + Math.random() * (lonMax - lonMin),
value: Math.floor(Math.random() * 100),
};
dataRaw.push(point);
}
// 生成數(shù)據(jù)
for (var i = 0; i < len; i++) {
var dataItem = dataRaw[i];
var point = {
x: Math.floor(((dataItem.lat - latMin) / (latMax - latMin)) * width),
y: Math.floor(((dataItem.lon - lonMin) / (lonMax - lonMin)) * height),
value: Math.floor(dataItem.value),
};
max = Math.max(max, dataItem.value);
points.push(point);
}
// 創(chuàng)建熱力圖
var heatmapInstance = h337.create({
container: document.querySelector(".heatmap"),
});
var data = {
max: max,
data: points,
};
heatmapInstance.setData(data);
// 將熱力圖添加到球體上(生成的熱力圖canvas元素類名為heatmap-canvas)
var canvas = document.getElementsByClassName("heatmap-canvas");
// console.log(canvas);
viewer.entities.add({
name: "heatmap",
rectangle: {
coordinates: Cesium.Rectangle.fromDegrees(
lonMin,
latMin,
lonMax,
latMax
),
material: new Cesium.ImageMaterialProperty({
image: canvas[0],
transparent: true,
}),
},
});
效果圖如下:

集成Turf
Cesium本身更側(cè)重于三維可視化,在空間分析方面會(huì)顯得薄弱些,當(dāng)然空間分析能力可以借助開源postGIS中的函數(shù)去實(shí)現(xiàn),然后將結(jié)果通過Cesium去呈現(xiàn)。這里我們不對(duì)postGIS進(jìn)行介紹,而是給大家介紹一個(gè)輕量級(jí)的用于空間分析的前端庫(kù),即Turf。Turf的定位是地理空間分析庫(kù),處理各種地圖算法;特點(diǎn)是離線計(jì)算、模塊化、快速。下面是一個(gè)簡(jiǎn)單的示例:計(jì)算兩點(diǎn)之間的距離。
var point1 = turf.point([144.834823, -37.771257]);
var point2 = turf.point([145.14244, -37.830937]);
var midpoint = turf.midpoint(point1, point2);
而下面的截圖是通過Turf、Cesium實(shí)現(xiàn)的點(diǎn)、線、面緩沖區(qū)分析結(jié)果,即借助了Turf的空間分析能力和Cesium的可視化能力。

部分核心代碼如下:
// 初始化點(diǎn)緩沖
function initPointBuffer() {
let point = [106.422638966289, 29.5698367125623];
addPoint(point);
let pointF = turf.point(point);
let buffered = turf.buffer(pointF, 60, { units: "meters" });
let coordinates = buffered.geometry.coordinates;
let points = coordinates[0];
let degreesArray = pointsToDegreesArray(points);
addBufferPolyogn(Cesium.Cartesian3.fromDegreesArray(degreesArray));
}
// 添加點(diǎn)
function addPoint(point) {
viewer.entities.add({
position: Cesium.Cartesian3.fromDegrees(point[0], point[1], 0),
point: {
pixelSize: 10,
heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
color: Cesium.Color.YELLOW,
outlineWidth: 3,
outlineColor: Cesium.Color.YELLOW.withAlpha(0.4),
},
});
}
// 添加緩沖面
function addBufferPolyogn(positions) {
viewer.entities.add({
polygon: {
hierarchy: new Cesium.PolygonHierarchy(positions),
material: Cesium.Color.RED.withAlpha(0.6),
classificationType: Cesium.ClassificationType.BOTH,
},
});
}
如果你覺得比較麻煩的話,網(wǎng)上也有大神基于cesium、turf、shpjs、proj4js等這些庫(kù)文件封裝好了CesiumVectorTile,GitHub地址為https://github.com/engineerhe/CesiumVectorTile,支持小數(shù)據(jù)量的geojson、shape文件矢量動(dòng)態(tài)切片,并且還能實(shí)現(xiàn)貼地效果。
獲取上述全部源代碼可訪問本人GitHub地址:https://github.com/ls870061011。