准备工作:
安装mars3d依赖:
npm i mars3d
npm i mars3d-heatmap (热力图,需要的话安装)
npm i -D copy-webpack-plugin
增加mars3d目录配置,修改vue.config.js中configureWebpack里的内容如下:
const CopyWebpackPlugin = require("copy-webpack-plugin");
const cesiumSourcePath = "node_modules/mars3d-cesium/Build/Cesium/"; // cesium库安装目录
const cesiumRunPath = "./mars3d-cesium/"; // cesium运行时路径
const timeStamp = Date.now()
module.exports = {
configureWebpack: (config) => {
const plugins = [
// 标识cesium资源所在的主目录,cesium内部资源加载、多线程等处理时需要用到
new webpack.DefinePlugin({
CESIUM_BASE_URL: JSON.stringify(path.join(config.output.publicPath, cesiumRunPath))
}),
// Cesium相关资源目录需要拷贝到系统目录下面(部分CopyWebpackPlugin版本的语法可能没有patterns)
new CopyWebpackPlugin([
{ from: path.join(cesiumSourcePath, 'Workers'), to: path.join(config.output.path, cesiumRunPath, 'Workers') },
{ from: path.join(cesiumSourcePath, 'Assets'), to: path.join(config.output.path, cesiumRunPath, 'Assets') },
{ from: path.join(cesiumSourcePath, 'ThirdParty'), to: path.join(config.output.path, cesiumRunPath, 'ThirdParty') },
{ from: path.join(cesiumSourcePath, 'Widgets'), to: path.join(config.output.path, cesiumRunPath, 'Widgets') }
])
]
if (process.env.NODE_ENV === "production") {
plugins.push(
new UglifyJSPlugin({
uglifyOptions: {
//删除注释
output: {comments: false},
warnings: false,
//删除console 和 debugger 删除警告
compress: {
drop_debugger: true,
drop_console: true
}
},
cache: true, // 启用文件缓存
sourceMap: false,//不生成调试文件
parallel: true // 使用多进程并行运行来提高构建速度
})
);
}
config.plugins = [...config.plugins, ...plugins]
//给打包的文件名加上时间戳,防止浏览器缓存
config.output.filename = `js/[name].${timeStamp}.js`
config.output.chunkFilename = `js/[name].${timeStamp}.js`
return {
module: { unknownContextCritical: false }, // 配置加载的模块类型,cesium时必须配置
plugins: plugins
}
},
//...其它配置
}
文章来源:https://uudwc.com/A/9vp2V
组件页:
<template>
<div class="r-map-3d">
<div class="map-box" :id="mapId"></div>
</div>
</template>
<script setup>
import {defineEmits, defineProps, onMounted, reactive, ref} from 'vue'
import mars3D from '@/plugins/mars3d/mapController'
const props = defineProps({
//中心点
center: {
type: Array,
default: () => [0, 0]
}
})
const emit = defineEmits(['onLoad']);
const mapId = ref(`map_${Date.now()}`)
const data = reactive({
map: null,
})
onMounted(() => {
data.map = new mars3D(mapId.value, props.center)
emit('onLoad', data.map)
})
</script>
<style scoped lang="scss">
.r-map-3d {
width: 100%;
height: 100%;
.map-box {
height: 100%;
width: 100%;
}
}
</style>
mapController.js:(地图操作类)
import {mapUrl, mapAk} from "@/config/baseURL";
//引入cesium基础库
import "mars3d-cesium/Build/Cesium/Widgets/widgets.css";
import * as Cesium from "mars3d-cesium";
//导入mars3d主库
import "mars3d/dist/mars3d.css";
import * as mars3d from "mars3d";
import "mars3d-heatmap"
export default class MapController {
constructor(mapId) {
this.ak = mapAk
this.mapId = mapId
/** 地图实例 */
this.instance = null
this.initMap()
}
/**
* 创建天地图层
* @param name 描述
* @auth Roffer
* @date 2022/9/29 15:50
*
*/
initMap() {
// 需要覆盖config.json中地图属性参数(当前示例框架中自动处理合并)
//option相关设置:http://mars3d.cn/apidoc.html#Map
const mapOptions = {
//scene相关设置:http://mars3d.cn/api/Map.html#.sceneOptions
scene: {
center: {lat: 29.068075, lng: 103.966252, alt: 82849.5, heading: 358.9, pitch: -28.4},
showSkyAtmosphere: false,
backgroundColor: '#000',
contextOptions: {
webgl: {
//通过canvas.toDataURL()实现截图需要将该项设置为true
preserveDrawingBuffer: true
}
},
cameraController: {
//相机最近视距,变焦时相机位置的最小量级(以米为单位),默认为1。该值是相机与地表(含地形)的相对距离。默认1.0
minimumZoomDistance: 1,
//相机最远视距,变焦时相机位置的最大值(以米为单位)。该值是相机与地表(含地形)的相对距离。默认50000000.0
maximumZoomDistance: 200000
},
globe: {
show: true,//是否显示地球
}
},
// control: {
// locationBar: {
// fps: true,//是否显示实时FPS帧率
// navigationHelpButton: true
// }
// },
}
this.instance = new mars3d.Map(this.mapId, mapOptions); //支持的参数请看API文档:http://mars3d.cn/api/Map.html
this.instance.basemap = 2017 // 蓝色底图
const mapLayer = mars3d.LayerUtil.create({
type: "wmts",
url: mapUrl,
//wmts服务元数据中指定的layer值
format: "image/png",
layer: "defaultLayer",
style: "default",
//跨域支持,在使用html2canvas截屏的时候,如果不设置该属性,会截不到地图内容
crossOrigin: 'Anonymous',
tileMatrixSetID: "GetTileMatrix",
minimumLevel: 3,
maximumLevel: 17,
minimumTerrainLevel: 1,
maximumTerrainLevel: 25,
zIndex: 2,
crs: "EPSG4490",
chinaCRS: "WGS84",
tileWidth: 256,
tileHeight: 256,
name: "影像",
extent: {
xmin: 96.8064823,
ymin: 25.68096106,
xmax: 109.1252279,
ymax: 34.75215408
}
})
this.instance.addLayer(mapLayer)
//获取右键菜单绘制完成数据
this.instance.on(mars3d.EventType.drawCreated, (e) => {
console.log(JSON.stringify(e.graphic));
})
}
/**
* 区域标记
* @param areaCode 区域编码
* @param filter 透明度,默认不透明,值范围:0~1
* @param popupState 点击地图popup显隐
* @auth Roffer
* @date 2022/11/10 19:03
*
*/
addAreaLayer(areaCode = '510100000000', filter = 1, styleCback,popupState=true) {
const geoJsonLayer = new mars3d.layer.GeoJsonLayer({
name: "成都市",
url: `/json/5101/${areaCode}.json`,
allowDrillPick: true,
symbol: {
type: "polygon",
styleOptions: {
materialType: mars3d.MaterialType.PolyGradient, // 重要参数,指定材质
// materialType: mars3d.MaterialType.Stripe, // 重要参数,指定材质
materialOptions: {
color: "#3388cc",
opacity: 0.7,
alphaPower: 1.3
},
// 面中心点,显示文字的配置
label: {
text: "{name}", // 对应的属性名称
opacity: 1,
font_size: 18,
color: "#fff",
font_family: "宋体",
outline: false,
scaleByDistance: true,
scaleByDistance_far: 20000000,
scaleByDistance_farValue: 0.1,
scaleByDistance_near: 1000,
scaleByDistance_nearValue: 1
}
},
callback: styleCback ? styleCback : function (attr, styleOpt) {
const randomHeight = (attr.childrenNum || 1) * 600 // 测试的高度
return {
materialOptions: {
color: attr.fill,
alphaPower: filter
// color: getColor()
},
height: 20,
diffHeight: randomHeight
}
}
},
popup:popupState ? "{name}" : false
})
this.instance.addLayer(geoJsonLayer)
const arrColor = ["rgb(15,176,255)", "rgb(18,76,154)", "#40C4E4", "#42B2BE", "rgb(51,176,204)", "#8CB7E5", "rgb(0,244,188)", "#139FF0"]
let index = 0
function getColor() {
return arrColor[++index % arrColor.length]
}
return geoJsonLayer
}
/**
* 添加自定义多边形
* @param geoJson geoJson数据
* @param color 多边形颜色
* @auth Roffer
* @date 2022/12/8 11:01
*
*/
addCustomLayer({pointList, color, text, popup, popupOptions, tooltip, tooltipOptions}) {
const graphicLayer = this.genGraphicLayer()
const graphic = new mars3d.graphic.PolygonEntity({
positions: pointList,
style: {
color,
opacity: 0.5,
outline: false,
outlineWidth: 3,
outlineColor: "#ffffff",
highlight: {
opacity: 0.8
},
label: {
show: false,
text,
font_size: 12,
color: "#ffffff",
distanceDisplayCondition: true,
distanceDisplayCondition_far: 500000,
distanceDisplayCondition_near: 0
}
},
popup,
popupOptions,
tooltip,
tooltipOptions
})
graphicLayer.addGraphic(graphic)
this.instance.addLayer(graphicLayer)
return graphicLayer
}
/**
* 添加一个自定义标注层
* @auth Roffer
* @date 2022/11/11 14:19
*
*/
genGraphicLayer() {
let graphicLayer = new mars3d.layer.GraphicLayer()
this.instance.addLayer(graphicLayer)
return graphicLayer
}
/**
* 添加自定义html标记点,适用于小于100条数据
* @param point 经纬度,示例:[123.1,22.444]
* @param html 要显示的html内容
* @param popup 自定义详细信息(String,Array,function)
* @param popupOptions 自定义详细信息参数设置(Object)详情:http://mars3d.cn/api/Popup.html#.StyleOptions
* @param tooltip 自定义鼠标悬浮提示信息(String,Array,function)
* @param tooltipOptions 自定义鼠标悬浮提示信息参数设置(Object)详情:http://mars3d.cn/api/Tooltip.html#.StyleOptions
* @param attr 附加自定义属性(可在点击时,通过e.target.attr获取)
* @param click 点击回调函数(返回点击的对象)
* @param style Object,覆盖style对象中的属性值
* @auth Roffer
* @date 2022/10/22 14:55
*
*/
addDivLayer({point, html, popup, popupOptions, tooltip, tooltipOptions, attr, click, style}, graphicLayer) {
const graphic = new mars3d.graphic.DivGraphic({
position: new mars3d.LngLatPoint(point[0], point[1], 915),
style: {
html,
horizontalOrigin: Cesium.HorizontalOrigin.LEFT,
verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
clampToGround: true,
offsetX: -20,
offsetY: -30,
...style
},
attr,
popup,
popupOptions,
tooltip,
tooltipOptions,
eventParent: false,//不冒泡事件
})
graphicLayer.addGraphic(graphic)
click && graphic.on(mars3d.EventType.click, click)
return graphicLayer
}
/**
* 批量添加html标标注
* @param list 数据,
* 格式为:
* [{
* point:[103.111,30.123],html:'<div>xxx</div>',attr:{},click:()=>{}
* },...]
* @auth Roffer
* @date 2022/11/11 14:00
*
*/
batchAddDivLayer(list = []) {
// 创建矢量数据图层
let graphicLayer = this.genGraphicLayer()
list.forEach(item => {
this.addDivLayer(item, graphicLayer)
})
return graphicLayer
}
/**
* 获取当前屏幕边界坐标
* @auth Roffer
* @date 2022/11/21 15:37
*
*/
getScreenXY() {
//屏幕边界坐标
let info = this.instance.viewer.camera.computeViewRectangle()
info.east = Cesium.Math.toDegrees(info.east)
info.north = Cesium.Math.toDegrees(info.north)
info.south = Cesium.Math.toDegrees(info.south)
info.west = Cesium.Math.toDegrees(info.west)
// east 104.58916517174933
// north 30.8081617249502
// south 30.48271217718593
// west 103.95342383654778
return info
}
/**
* 添加自定义标记点
* @param data 参数对象,包含以下:
* url 请求数据的接口地址(url、data二选一)
* dataColumn data字段key(url时指定)
* data 请求数据的接口地址(url、data二选一)
* lngColumn 经纬字段key
* latColumn 纬纬字段key
* altColumn 高度字段key
* positionIconImg 显示的图标图片
* showGroup 是否显示聚合分组数据,默认true显示
* text 显示的名称字段key
* @param popup 详细信息对象
* key:对象中的key值
* item[key]:显示的名称 eg:{name:'名称'}
* @auth Roffer
* @date 2022/10/22 14:55
*
*/
addBusineDataLayer(data, popup) {
let option = {
showGroup: true,
...data
}
// 创建矢量数据图层(业务数据图层)
let busineDataLayer = new mars3d.layer.BusineDataLayer({
...option,
symbol: {
type: "billboard", // 对应是 mars3d.graphic.BillboardEntity
styleOptions: {
image: option.positionIconImg,
horizontalOrigin: Cesium.HorizontalOrigin.CENTER,
verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
scaleByDistance: new Cesium.NearFarScalar(1000, 0.7, 5000000, 0.3),
visibleDepth: false,
// label: {
// text: `{${data.text || 'text'}}`,
// font_size: 13,
// color: Cesium.Color.AZURE,
// outline: true,
// outlineColor: Cesium.Color.BLACK,
// outlineWidth: 2,
// horizontalOrigin: Cesium.HorizontalOrigin.LEFT,
// verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
// pixelOffset: new Cesium.Cartesian2(10, 0), // 偏移量
// distanceDisplayCondition: new Cesium.DistanceDisplayCondition(0.0, 80000)
// }
}
},
// 点的聚合配置
clustering: {
enabled: option.showGroup,
pixelRange: 20,
clampToGround: false,
opacity: 1,
// getImage: function (count) { //getImage是完全自定义方式
// let colorIn
// if (count < 10) {
// colorIn = 'rgba(110, 204, 57, 0.6)'
// } else if (count < 100) {
// colorIn = 'rgba(240, 194, 12, 0.6)'
// } else {
// colorIn = 'rgba(241, 128, 23, 0.6)'
// }
// return mars3d.Util.getCircleImage(count, {
// color: colorIn,
// radius: 30,
// })
// },
}
})
this.instance.addLayer(busineDataLayer)
//包含详情
if (popup) {
//详细信息弹窗
busineDataLayer.bindPopup(function (event) {
const item = event.graphic?.attr
if (!item) {
return false
}
const tabData = [
`<table style="width: auto;">
<tr><th scope="col" colspan="2" style="text-align:center;font-size:15px;">${item[option.text]}</th></tr>`
]
for (let key in popup) {
tabData.push(`<tr>`)
tabData.push(`<td>${popup[key]}</td><td>${item[key]}</td>`)
tabData.push(`</tr>`)
}
tabData.push('</table>')
return tabData.join('')
})
busineDataLayer.bindTooltip(function (event) {
const item = event.graphic?.attr
if (!item) {
return false
}
const tabData = [
`<table style="width: auto;">
<tr><th scope="col" colspan="2" style="text-align:center;font-size:15px;">${item[option.text]}</th></tr>`
]
for (let key in popup) {
tabData.push(`<tr>`)
tabData.push(`<td>${popup[key]}</td><td>${item[key]}</td>`)
tabData.push(`</tr>`)
}
tabData.push('</table>')
return tabData.join('')
})
}
// 单击事件
busineDataLayer.on(mars3d.EventType.click, (event) => {
if (this.instance.camera.positionCartographic.height > 1000) {
const graphic = event.graphic
if (graphic) {
// 单击了具体的点对象
// graphic.closePopup()
const position = graphic.positionShow
this.instance.flyToPoint(position, {
radius: 1000, // 距离目标点的距离
duration: 3,
complete: function (e) {
// 飞行完成回调方法
// graphic.openPopup()
}
})
} else {
// 单击了聚合的点
const arrEntity = event.pickedObject.id
this.instance.flyTo(arrEntity)
}
}
})
return busineDataLayer
}
/**
* 两点连线(弧线)
* @param startPoint 起始经纬度
* @param endPoint 结束经纬度
* @param attr 显示的信息对象(object,key、value格式)
* @param popup 点击时显示的信息
* @param popupOptions 点击时显示的信息参数设置对象,如可以设置偏移量{offsetX:-10,offsetY:-10},更多参数:http://mars3d.cn/api/Popup.html#.StyleOptions
* @param tooltip hover时显示的信息
* @param tooltipOptions hover时显示的信息参数设置对象,如可以设置偏移量{offsetX:-10,offsetY:-10},更多参数:http://mars3d.cn/api/Tooltip.html#.StyleOptions
* @param lineColor 线条的颜色(默认:#1a9850)
* @auth Roffer
* @date 2022/12/3 11:04
*
*/
addArc({startPoint, endPoint, attr, popup, popupOptions, tooltip, tooltipOptions, lineColor}, graphicLayer) {
graphicLayer = graphicLayer || this.genGraphicLayer()
const start = Cesium.Cartesian3.fromDegrees(...startPoint, 42.31)
const end = Cesium.Cartesian3.fromDegrees(...endPoint, 37.53)
const positions = mars3d.PolyUtil.getLinkedPointList(start, end, 20000, 50) // 计算曲线点
const graphic = new mars3d.graphic.PolylineEntity({
positions: positions,
style: {
width: 10,
// 动画线材质
materialType: mars3d.MaterialType.LineFlow,
materialOptions: {
image: require('@/assets/img/map_icon/line-arrow-blue.png'),
color: lineColor || '#1a9850',
mixt: false,
speed: 20,
repeat: new Cesium.Cartesian2(5, 1)
}
},
attr,
popup,
popupOptions,
tooltip,
tooltipOptions
})
graphicLayer.addGraphic(graphic)
return graphicLayer
}
/**
* 热力图
* @param pointData 经纬度数组 [{lng:123,lat:456},...]
* @auth Roffer
* @date 2022/11/11 14:58
*
*/
addHeatLayer(pointData) {
// 热力图 图层
let heatLayer = new mars3d.layer.HeatLayer({
positions: pointData,
// rectangle: rectangle,
// 以下为热力图本身的样式参数,可参阅api:https://www.patrick-wied.at/static/heatmapjs/docs.html
heatStyle: {
radius: 40,
blur: 0.85
},
// 以下为矩形矢量对象的样式参数
style: {
arc: true, // 是否为曲面
height: 100.0,
opacity: 0.6,
// classificationType: Cesium.ClassificationType.CESIUM_3D_TILE,
// clampToGround: true
}
})
this.instance.addLayer(heatLayer)
return heatLayer
}
/**
* 删除layer
* @param layer layer对象
* @auth Roffer
* @date 2022/11/11 14:26
*
*/
removeLayer(layer) {
this.instance.removeLayer(layer)
}
}
使用:
<r-map-3d @onLoad="mapLoad"/>
<script setup>
let map = null//地图实例
let areaLayer = null//图层的操作最好不要定义在vue3的响应式对象中,否则会引起页面卡顿
/** 地图加载完成 */
const mapLoad = (instance) => {
map = instance
areaLayer = map.addAreaLayer('510100000000')
}
</script>
最后附上天地图mapUrl地址:
export const mapAk = 'xxxx'//请自行去官方申请
//浅色底图
// export const mapUrl = `http://www.scgis.net/services/newtianditudlg/WMTS?ak=${mapAk}`
//暗色底图
export const mapUrl = `http://www.scgis.net/services/tdtdarkmap/WMTS?ak=${mapAk}`
省市区地图json数据,请移步到https://geojson.cn/ 根据业务需求下载对应的json数据
mars3d官网 文章来源地址https://uudwc.com/A/9vp2V