需求:要求將GLTF三維模型放到地圖上展示,並且添加各種圖標和線進行標注。
用CesiumJS地圖庫實現代碼如下:
引入CesiumJS庫
1、直接clone源碼包,在index.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" /> <meta name="description" content="Create 3D models using glTF."> <title>Chemical plant</title> <script type="text/javascript" src="../../Cesium-1.77/Build/Cesium/Cesium.js"></script> <link href="../../Cesium-1.77/Build/Cesium/Widgets/widgets.css" rel="stylesheet"> </head> <body> <div id="cesiumContainer"></div>
2、參考官網說明(https://www.cesium.com/docs/tutorials/cesium-and-webpack/)用npm安裝
貼下package.json和webpack.config.js:
package.json
{ "name": "cesium-webpack-app", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "build": "node_modules/.bin/webpack --config webpack.config.js", "start": "node_modules/.bin/webpack serve --config webpack.config.js --open" }, "author": "", "license": "ISC", "devDependencies": { "cesium": "^1.78.0", "copy-webpack-plugin": "^7.0.0", "css-loader": "^5.0.2", "html-webpack-plugin": "^5.2.0", "style-loader": "^2.0.0", "url-loader": "^4.1.1", "webpack": "^5.23.0", "webpack-cli": "^4.5.0", "webpack-dev-server": "^3.11.2" } }
webpack.config.js
const path = require('path'); const webpack = require('webpack'); const HtmlWebpackPlugin = require('html-webpack-plugin'); const CopyWebpackPlugin = require('copy-webpack-plugin'); const cesiumSource = 'node_modules/cesium/Source'; const cesiumWorkers = '../Build/Cesium/Workers'; module.exports = { context: __dirname, entry: { app: './src/index.js' }, output: { filename: '[name].js', path: path.resolve(__dirname, 'dist'), sourcePrefix: '' }, amd: { toUrlUndefined: true }, module: { rules: [{ test: /\.css$/, use: ['style-loader', 'css-loader'] }, { test: /\.(png|gif|jpg|jpeg|svg|xml|json)$/, use: ['url-loader'] }] }, plugins: [ new HtmlWebpackPlugin({ template: 'src/index.html' }), new CopyWebpackPlugin({ patterns: [ { from: path.join(cesiumSource, cesiumWorkers), to: 'Workers' }, { from: path.join(cesiumSource, 'Assets'), to: 'Assets' }, { from: path.join(cesiumSource, 'Widgets'), to: 'Widgets' } ] }), new webpack.DefinePlugin({ CESIUM_BASE_URL: JSON.stringify('') }) ], devServer: { contentBase: path.join(__dirname, "dist") }, resolve: { alias: { cesium: path.resolve(__dirname, cesiumSource) }, fallback: { fs: false } } };
以上2種方式經測試都不需要在官網申請token,直接使用api即可。
初始化地圖:
const imageryProvider = new Cesium.UrlTemplateImageryProvider({ url: "http://webst01.is.autonavi.com/appmaptile?lang=zh_cn&style=7&x={x}&y={y}&z={z}", tilingScheme: new Cesium.WebMercatorTilingScheme(), fileExtension: 'png', minimumLevel: 0, maximumLevel: 20, }); const viewer = new Cesium.Viewer("cesiumContainer", { imageryProvider: imageryProvider, animation: false, //是否顯示動畫控件 homeButton: false, //是否顯示home鍵 geocoder: false, //是否顯示地名查找控件,如果設置為true,則無法查詢 baseLayerPicker: false, //是否顯示圖層選擇控件 timeline: false, //是否顯示時間線控件 fullscreenButton: false, //是否全屏顯示 scene3DOnly: true, //如果設置為true,則所有幾何圖形以3D模式繪制以節約GPU資源 infoBox: false, //是否顯示點擊要素之后顯示的信息 sceneModePicker: false, //是否顯示投影方式控件 三維/二維 navigationInstructionsInitiallyVisible: false, navigationHelpButton: false, //是否顯示幫助信息控件 selectionIndicator: false, //是否顯示指示器組件 }); viewer._cesiumWidget._creditContainer.style.display = "none"; //添加文字標注 viewer.imageryLayers.addImageryProvider(new Cesium.WebMapTileServiceImageryProvider({ url: "http://t0.tianditu.com/cva_w/wmts?service=wmts&request=GetTile&version=1.0.0&LAYER=cva&tileMatrixSet=w&TileMatrix={TileMatrix}&TileRow={TileRow}&TileCol={TileCol}&style=default&format=tiles&tk=TOKEN", layer: "tdtAnnoLayer", style: "default", format: "image/jpeg", tileMatrixSetID: "GoogleMapsCompatible", show: false, })); viewer.scene.primitives.add(Cesium.createOsmBuildings()); viewer.scene.highDynamicRange = false; viewer.shadows = false;
{ //修改鼠標控制地圖視圖的默認行為:左鍵拖動,右鍵旋轉
viewer.scene.screenSpaceCameraController.zoomEventTypes = [
Cesium.CameraEventType.WHEEL,
Cesium.CameraEventType.MIDDLE_DRAG,
Cesium.CameraEventType.PINCH,
];
viewer.scene.screenSpaceCameraController.tiltEventTypes = [
Cesium.CameraEventType.RIGHT_DRAG,
Cesium.CameraEventType.PINCH,
{
eventType: Cesium.CameraEventType.RIGHT_DRAG,
modifier: Cesium.KeyboardEventModifier.CTRL,
},
{
eventType: Cesium.CameraEventType.MIDDLE_DRAG,
modifier: Cesium.KeyboardEventModifier.CTRL,
},
];
}
createGltfModel("../../data/gltf/NewFile.gltf", 117.628293, 23.798717, 0.0, { modelScale: 1, //模型放大倍數 lightColor: new Cesium.Cartesian3(20.0, 20.0, 20.0), //模型的材質亮度 position: { //地圖初始化位置,坐標是3維笛卡爾坐標 x: -2707823.1735314624, y: 5173260.303294086, z: 2558159.506208664, }, orientation: { heading: 4.735189003200103, pitch: -0.4113143959917751, roll: 6.2804863005660785, }, }); function createGltfModel(url, lng, lat, height, { modelScale, lightColor, position, orientation } = { modelScale: 1, lightColor: new Cesium.Cartesian3(0, 0, 0), position: { x: 0, y: 0, z: 0 }, orientation: { heading: 0, pitch: 0, roll: 0 } }) { viewer.entities.removeAll(); let ellipsoid = viewer.scene.globe.ellipsoid; let cart3 = ellipsoid.cartographicToCartesian(Cesium.Cartographic.fromDegrees(lng, lat, height)); let modelMatrix = Cesium.Transforms.eastNorthUpToFixedFrame(cart3); let model = viewer.scene.primitives.add(Cesium.Model.fromGltf({ url, modelMatrix, scale: 1, //放大倍數 lightColor,//gltf模型加載后比較暗,這個起到增大模型材質亮度的作用 })); let initialPosition = new Cesium.Cartesian3(position.x, position.y, position.z); // 相機的位置 let homeCameraView = { destination: initialPosition, // 相機的位置 orientation, }; viewer.scene.camera.setView(homeCameraView); /** * 這個時間是我調試的結果,模型不至於很暗 * 主要是時間要調到太陽照射到整個模型區域,日期沒有多大影響 */ let utc = Cesium.JulianDate.fromDate(new Date("2021/02/20 03:40:00"));//UTC viewer.clock.currentTime = Cesium.JulianDate.addHours(utc, 8, new Cesium.JulianDate()); }
抗鋸齒處理:
//抗鋸齒處理 { viewer.scene.fxaa = true viewer.scene.postProcessStages.fxaa.enabled = true; let supportsImageRenderingPixelated = viewer.cesiumWidget._supportsImageRenderingPixelated; if (supportsImageRenderingPixelated) { let vtxf_dpr = window.devicePixelRatio; while (vtxf_dpr >= 2.0) { vtxf_dpr /= 2.0; } viewer.resolutionScale = vtxf_dpr; } }
添加圖片:
//添加圖片 { const iconMarks = [ { id: 'factory1', position: [117.62514171688602, 23.800514411219126, 40], image: '../../data/icons/廢氣-C.png', }, { id: 'factory2', position: [117.62441672316956, 23.801488899527932, 40], image: '../../data/icons/廢氣-C.png', }, { id: 'camera1', position: [117.62438331048037, 23.80172986514075, 20], image: '../../data/icons/攝像頭-C.png', width: 13, height: 18 }, { id: 'camera2', position: [117.6251283858732, 23.801983468675637, 10], image: '../../data/icons/攝像頭-C.png', width: 13, height: 18 } ]; for (const mark of iconMarks) { let width = mark.width || 30; let height = mark.height || 30; viewer.entities.add({ id: mark.id, name: 'mark', position: Cesium.Cartesian3.fromDegrees(...mark.position), billboard: { // 圖像地址,URI或Canvas的屬性 image: mark.image, // 設置顏色和透明度 // color: Cesium.Color.WHITE.withAlpha(1), // 高度(以像素為單位) height, // 寬度(以像素為單位) width, // 逆時針旋轉 rotation: 0, // 大小是否以米為單位 sizeInMeters: false, // 相對於坐標的垂直位置 verticalOrigin: Cesium.VerticalOrigin.CENTER, // 相對於坐標的水平位置 horizontalOrigin: Cesium.HorizontalOrigin.LEFT, // 該屬性指定標簽在屏幕空間中距此標簽原點的像素偏移量 pixelOffset: new Cesium.Cartesian2(-width / 2, -height / 2), // 應用於圖像的統一比例。比例大於會1.0放大標簽,而比例小於會1.0縮小標簽。 scale: 1.0, // 是否顯示 show: true } }); } }
添加線:
//添加線 { const markLines = [ { id: 'line1', positions: [[117.62514171688602, 23.800514411219126, 0], [117.62514171688602, 23.800514411219126, 40]], }, { id: 'line2', positions: [[117.62441672316956, 23.801488899527932, 0], [117.62441672316956, 23.801488899527932, 40]], }, { id: 'line3', positions: [[117.62441672316956, 23.801488899527932, 20], [117.62438331048037, 23.80172986514075, 20], [117.6251283858732, 23.801983468675637, 10], [117.62516689671625, 23.802117335288763, 10]] } ]; for (const line of markLines) { let polyline = new Cesium.PolylineGraphics(); polyline.material = new Cesium.ColorMaterialProperty(new Cesium.Color(0.18, 0.67, 1, 1)); polyline.width = new Cesium.ConstantProperty(2); polyline.arcType = new Cesium.ConstantProperty( Cesium.ArcType.NONE ); let coordArr = line.positions.map(e => Cesium.Cartesian3.fromDegrees(...e)); console.log('coordArr', coordArr); polyline.positions = new Cesium.ConstantProperty(coordArr); let entity = new Cesium.Entity({ id: line.id, show: true, polyline, name: 'line', }); viewer.entities.add(entity); } }
添加事件:
//事件 { let handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas); handler.setInputAction(function (evt) { let pick = viewer.scene.pick(evt.position); // //選中某模型 if (pick && pick.id) { alert(pick.id._id) } let cartesian = viewer.camera.pickEllipsoid(evt.position, viewer.scene.globe.ellipsoid); let cartographic = Cesium.Cartographic.fromCartesian(cartesian); let lng = Cesium.Math.toDegrees(cartographic.longitude);//經度值 let lat = Cesium.Math.toDegrees(cartographic.latitude);//緯度值 let mapPosition = { x: lng, y: lat, z: cartographic.height };//cartographic.height的值始終為零。 // console.log('mapPosition: ', mapPosition); console.log('lng: ', lng, 'lat: ', lat);//拾取坐標 }, Cesium.ScreenSpaceEventType.LEFT_CLICK); handler.setInputAction(evt => { let pick = viewer.scene.pick(evt.endPosition); //選中某模型 if (pick && pick.id && pick.id._name === 'mark') { // console.log('pick', pick.id._id);
// 修改鼠標指針樣式
viewer._container.style.cursor = 'pointer'; } else { viewer._container.style.cursor = ''; } }, Cesium.ScreenSpaceEventType.MOUSE_MOVE) }
效果: