vue实现mapbox自定义3D模板
-
使用npm进行安装js(默认安装最新版) npm地址
1.npm i mapbox-gl 2.npm i maptalks 3.npm i maptalks.mapboxgl 4.npm i @mapbox/mapbox-gl-language 5.npm i three
-
引用我们需要的js
const mapboxgl = require('mapbox-gl'); const maptalks = require('maptalks'); const maptalksM = require('maptalks.mapboxgl'); const MapboxLanguage = require("@mapbox/mapbox-gl-language"); const THREE = window.THREE = require('three'); const {ColladaLoader} = require('three/examples/jsm/loaders/ColladaLoader');//在three.js包下
-
定义data数据
data() { return { positionLocation: [121.509758, 31.300800], zoom: 16, pitch: 60, //地图的角度,不写默认是0,取值是0-60度,一般在3D中使用 bearing: 10, antialias: false, //抗锯齿,通过false关闭提升性能 maxPitch: 60, container: 'map', lonlats: [], baseUrl: "api/service-count", markerRe: [],//标点 x_PI: 3.14159265358979324 * 3000.0 / 180.0, PI: 3.1415926535897932384626, a: 6378245.0, ee: 0.00669342162296594323, layer: "", }; },
-
定义一个容器
<template> <div id="map"> </div> </template> <style scoped> /* mapbox css*/ @import url('https://api.tiles.mapbox.com/mapbox-gl-js/v0.44.2/mapbox-gl.css'); #map { position: absolute; top: 40px; bottom: 0; width: 100%; margin-left: -24px; } </style>
-
完整的js代码 (去mapboxgl官网中注册账号,并新建一个token)
/** * 判断坐标是否在国内 百度坐标转gps坐标 start */ out_of_china(lng, lat) { return (lng < 72.004 || lng > 137.8347) || ((lat < 0.8293 || lat > 55.8271) || false); }, /** * 提供了百度坐标(BD09)、国测局坐标(火星坐标,GCJ02)、和WGS84坐标系之间的转换 * 即 百度 转 谷歌、高德 */ bd09togcj02(bd_lon, bd_lat) { var x_pi = 3.14159265358979324 * 3000.0 / 180.0; var x = bd_lon - 0.0065; var y = bd_lat - 0.006; var z = Math.sqrt(x * x + y * y) - 0.00002 * Math.sin(y * x_pi); var theta = Math.atan2(y, x) - 0.000003 * Math.cos(x * x_pi); var gg_lng = z * Math.cos(theta); var gg_lat = z * Math.sin(theta); return [gg_lng, gg_lat] }, /** * GCJ02 转换为 WGS84 */ gcj02towgs84(lng, lat) { if (this.out_of_china(lng, lat)) { return [lng, lat] } else { var dlat = this.transformlat(lng - 105.0, lat - 35.0); var dlng = this.transformlng(lng - 105.0, lat - 35.0); var radlat = lat / 180.0 * this.PI; var magic = Math.sin(radlat); magic = 1 - this.ee * magic * magic; var sqrtmagic = Math.sqrt(magic); dlat = (dlat * 180.0) / ((this.a * (1 - this.ee)) / (magic * sqrtmagic) * this.PI); dlng = (dlng * 180.0) / (this.a / sqrtmagic * Math.cos(radlat) * this.PI); var mglat = lat + dlat; var mglng = lng + dlng; return [lng * 2 - mglng, lat * 2 - mglat] } }, transformlat(lng, lat) { var ret = -100.0 + 2.0 * lng + 3.0 * lat + 0.2 * lat * lat + 0.1 * lng * lat + 0.2 * Math.sqrt(Math.abs(lng)); ret += (20.0 * Math.sin(6.0 * lng * this.PI) + 20.0 * Math.sin(2.0 * lng * this.PI)) * 2.0 / 3.0; ret += (20.0 * Math.sin(lat * this.PI) + 40.0 * Math.sin(lat / 3.0 * this.PI)) * 2.0 / 3.0; ret += (160.0 * Math.sin(lat / 12.0 * this.PI) + 320 * Math.sin(lat * this.PI / 30.0)) * 2.0 / 3.0; return ret }, transformlng(lng, lat) { var ret = 300.0 + lng + 2.0 * lat + 0.1 * lng * lng + 0.1 * lng * lat + 0.1 * Math.sqrt(Math.abs(lng)); ret += (20.0 * Math.sin(6.0 * lng * this.PI) + 20.0 * Math.sin(2.0 * lng * this.PI)) * 2.0 / 3.0; ret += (20.0 * Math.sin(lng * this.PI) + 40.0 * Math.sin(lng / 3.0 * this.PI)) * 2.0 / 3.0; ret += (150.0 * Math.sin(lng / 12.0 * this.PI) + 300.0 * Math.sin(lng / 30.0 * this.PI)) * 2.0 / 3.0; return ret }, baiMapGps(long, lat) { let bd09togcj = this.bd09togcj02(long, lat);//百度转火星 let baiMapGps = this.gcj02towgs84(bd09togcj[0], bd09togcj[1]);//GCJ02 转换为 WGS84 return baiMapGps; }, /** * 百度坐标转gps坐标 end */ initMarker() { this.$http({ url: this.baseUrl + "/xxxx/xx", method: "GET" }) .then(response => { this.lonlats = response.data[0]; this.marker(); this.initMap(); }) .catch(e => { console.log(e); }); }, initMap() { mapboxgl.accessToken = 'pk.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'; var map = new mapboxgl.Map({ container: 'map', style: 'mapbox://styles/mapbox/dark-v9', maxPitch: this.maxPitch, center: this.positionLocation, zoom: this.zoom, pitch: this.pitch, bearing: this.bearing }); /** * 设置中文 */ map.addControl(new MapboxLanguage({ defaultLanguage: 'zh' })); /** * 添加 3D模板 start */ // 确保模型在地图上正确地理参考的参数 var modelOrigin = [121.509758, 31.300800]; var modelAltitude = 0; var modelRotate = [Math.PI / 2, 0, 0]; var modelAsMercatorCoordinate = mapboxgl.MercatorCoordinate.fromLngLat( modelOrigin, modelAltitude ); // 转换参数以在地图上定位,旋转和缩放3D模型 var modelTransform = { translateX: modelAsMercatorCoordinate.x, translateY: modelAsMercatorCoordinate.y, translateZ: modelAsMercatorCoordinate.z, rotateX: modelRotate[0], rotateY: modelRotate[1], rotateZ: modelRotate[2], /* Since our 3D model is in real world meters, a scale transform needs to be * applied since the CustomLayerInterface expects units in MercatorCoordinates. */ scale: modelAsMercatorCoordinate.meterInMercatorCoordinateUnits() }; // 根据CustomLayerInterface为3D模型配置自定义层 //var THREE = window.THREE; //= require('three'); var customLayer = { id: '3d-model', type: 'custom', renderingMode: '3d', onAdd: function (map, gl) { this.camera = new THREE.Camera(); this.scene = new THREE.Scene(); // 创建两个three.js灯来照亮模型 var directionalLight = new THREE.DirectionalLight(0xcccccc); directionalLight.position.set(0, -70, 100).normalize(); this.scene.add(directionalLight); var directionalLight2 = new THREE.DirectionalLight(0xcccccc); directionalLight2.position.set(0, 70, 100).normalize(); this.scene.add(directionalLight2); // 使用three.js GLTF加载器将3D模型添加到three.js场景 var loader = new ColladaLoader; loader.load('./dist/xxx.dae', function (gltf) { this.scene.add(gltf.scene); }.bind(this), // called while loading is progressing function (xhr) { //console.log("xhr:", xhr); //console.log((xhr.loaded / xhr.total * 100) + '% loaded'); }, // called when loading has errors function (error) { console.log("errot:", error); } ); this.map = map; // 对three.js使用Mapbox GL JS地图画布 this.renderer = new THREE.WebGLRenderer({ canvas: map.getCanvas(), context: gl, antialias: true }); this.renderer.autoClear = false; }, render: function (gl, matrix) { var rotationX = new THREE.Matrix4().makeRotationAxis( new THREE.Vector3(1, 0, 0), modelTransform.rotateX ); var rotationY = new THREE.Matrix4().makeRotationAxis( new THREE.Vector3(0, 1, 0), modelTransform.rotateY ); var rotationZ = new THREE.Matrix4().makeRotationAxis( new THREE.Vector3(0, 0, 1), modelTransform.rotateZ ); var m = new THREE.Matrix4().fromArray(matrix); var l = new THREE.Matrix4() .makeTranslation( modelTransform.translateX, modelTransform.translateY, modelTransform.translateZ ) .scale( new THREE.Vector3( modelTransform.scale, -modelTransform.scale, modelTransform.scale ) ) .multiply(rotationX) .multiply(rotationY) .multiply(rotationZ); this.camera.projectionMatrix = m.multiply(l); this.renderer.state.reset(); this.renderer.render(this.scene, this.camera); this.map.triggerRepaint(); } }; /** * 添加 3D模板 end */ /** * 添加 热力图 start */ let lonlat = this.lonlats.lonlat; let arrList = []; for (let i = 0; i < lonlat.length; i++) { let aa = { type: "Feature", properties: { dbh: Math.ceil(Math.random() * 100) }, geometry: { type: "Point", coordinates: this.baiMapGps(lonlat[i].lon, lonlat[i].lat) } }; arrList.push(aa); } var geojson = { type: "FeatureCollection", features: arrList }; /* 摄像头数据 */ let cameraGeojson = { type: "FeatureCollection", features: [ { type: 'Feature', properties: { dbh: '摄像头1' }, geometry: { type: 'Point', coordinates: [121.510758, 31.301800], } }, ] }; map.on('load', function () { /* 3D 模板 */ map.addLayer(customLayer, 'waterway-label'); /* 摄像头 */ map.loadImage( './dist/2.png', function (error, image) { if (error) throw error; map.addImage('camera', image); map.addSource('point', { 'type': 'geojson', 'data': cameraGeojson }); map.addLayer({ id: 'points', type: 'symbol', source: 'point', layout: { 'icon-image': 'camera', 'icon-size': 1.0 } }); map.on('click', 'points', function (e) { alert(e.features[0].properties.dbh); }); }); /* 热力图 */ map.addSource('trees', { type: 'geojson', data: geojson }); // add heatmap layer here // add circle layer here map.addLayer({ id: 'trees-heat', type: 'heatmap', source: 'trees', maxzoom: 15, paint: { // increase weight as diameter breast height increases 'heatmap-weight': { property: 'dbh', type: 'exponential', stops: [ [1, 0], [62, 1] ] }, // increase intensity as zoom level increases 'heatmap-intensity': { stops: [ [11, 1], [15, 3] ] }, // assign color values be applied to points depending on their density 'heatmap-color': [ 'interpolate', ['linear'], ['heatmap-density'], 0, 'rgba(236,222,239,0)', 0.2, 'rgb(208,209,230)', 0.4, 'rgb(166,189,219)', 0.6, 'rgb(103,169,207)', 0.8, 'rgb(28,144,153)', 1.0, 'rgb(219,63,99)' ], // increase radius as zoom increases 'heatmap-radius': { stops: [ [11, 15], [15, 20] ] }, // decrease opacity to transition into the circle layer 'heatmap-opacity': { default: 1, stops: [ [14, 1], [15, 0] ] }, } }, 'waterway-label'); map.addLayer({ id: 'trees-point', type: 'circle', source: 'trees', minzoom: 14, paint: { // increase the radius of the circle as the zoom level and dbh value increases 'circle-radius': { property: 'dbh', type: 'exponential', stops: [ [{zoom: 15, value: 1}, 5], [{zoom: 15, value: 62}, 10], [{zoom: 22, value: 1}, 20], [{zoom: 22, value: 62}, 50], ] }, 'circle-color': { property: 'dbh', type: 'exponential', stops: [ [0, 'rgba(236,222,239,0)'], [10, 'rgb(236,222,239)'], [20, 'rgb(208,209,230)'], [30, 'rgb(166,189,219)'], [40, 'rgb(103,169,207)'], [50, 'rgb(28,144,153)'], [60, 'rgb(1,108,89)'], [80, 'rgb(232,78,109)'], ] }, 'circle-stroke-color': 'white', 'circle-stroke-width': 1, 'circle-opacity': { stops: [ [14, 0], [15, 1] ] } } }, 'waterway-label'); map.on('click', 'trees-point', function (e) { var proup = new mapboxgl.Popup() proup.setLngLat(e.features[0].geometry.coordinates) proup.setHTML('<div>当前人数:'+ e.features[0].properties.dbh +'</div> ') proup.addTo(map); }); }); /** * 热力图 end */ }