声明:本文涉及图文和模型素材仅用于个人学习、研究和欣赏,请勿二次修改、非法传播、转载、出版、商用、及进行其他获利行为。
🍟更新日志
- 2023年10月23日 增加地图和公交车的联动
- 2023年10月24日 添加详细的文字说明
- 2023年11月6日 增加左侧栏目
- 2023年11月8日 地图增加建筑白膜和公交OD线位置纠偏
- 2024年1月26日(29日) 增加开场动画
- 2024年2月5日 地图增加增加北京水系
- 2024年6月12日 增加公交实时进度样式,优化性能和增加开屏窗口动画,增加选中公交线地图公交线动画效果
🚦制作不易,请点赞 关注 收藏😁
🍗前言
首先看一下我们要完成的效果图(效果图来自thingjs),大概我们要按照这个来完成。这里我们选择的是mars3d开发数字孪生,Mars3D三维可视化平台 是火星科技研发的一款基于 WebGL 技术实现的三维客户端开发平台,基于Cesium优化提升与B/S架构设计,支持多行业扩展的轻量级高效能GIS开发平台,能够免安装、无插件地在浏览器中高效运行,并可快速接入与使用多种GIS数据和三维模型,呈现三维空间的可视化,完成平台在不同行业的灵活应用。只要你是web前端就可以开发,门槛更低
🍖开发语言
🍝头部组件(HeadTitle)
🍛效果图
🍤地图
首先加载一个地图
basemaps: [
{
name: "电子地图",
icon: "img/basemaps/google_vec.png",
type: "xyz",
url:
"https://webrd01.is.autonavi.com/appmaptile?lang=zh_cn&size=1&scale=1&style=7&x={x}&y={y}&z={z}",
show: true,
},
],
效果图的地图是黑色的,需要使用瓦片颜色滤镜GaodeLayer,地图是灰黑色,显的更加科技感
tileLayer = new mars3d.layer.GaodeLayer({
layer: "vec",
invertColor: true,
filterColor: "#485468",
brightness: 0.6,
contrast: 1.8,
gamma: 0.3,
hue: 1,
saturation: 0,
});
map.addLayer(tileLayer);
🍱效果
增加开场动画(2024/1/26更新)
// 开场动画
const startAnimation = () => {
map.flyHome({ duration: 0 });
map.openFlyAnimation({
callback: function () {
// 动画结束执行加载模型
initBuilding();
},
});
};
🍣北京公交线(OD线)
使用PolylineCombine图层,这里调节的一个是线的宽度和增加一个线条需要的背景图片,其他复制粘贴就行
// 北京公交线(OD线)
const busLine = () => {
// 创建矢量数据图层
graphicLayer = new mars3d.layer.GraphicLayer();
map.addLayer(graphicLayer);
let data = bjgjJson;
let busLines = [];
data.forEach(function (busLine, idx) {
let prevPt;
const points = [];
for (let i = 0; i < busLine.length; i += 2) {
let pt = [busLine[i], busLine[i + 1]];
if (i > 0) {
pt = [prevPt[0] + pt[0], prevPt[1] + pt[1]];
}
prevPt = pt;
const longitude = pt[0] / 1e4;
const latitude = pt[1] / 1e4;
const cart = Cesium.Cartesian3.fromDegrees(longitude, latitude, 100.0);
points.push(cart);
}
busLines.push({
positions: points,
style: {
width: 0.3,
materialType: mars3d.MaterialType.LineFlow,
image: getAssetsFile("lightFlow_strip07.png"),
speed: 2 + 1.0 * Math.random(),
},
});
});
// 多个线对象的合并渲染。
const graphic = new mars3d.graphic.PolylineCombine({
instances: busLines,
});
graphicLayer.addGraphic(graphic);
};
公交线纠偏(2023/11/8更新)
从上面的gif里可以看到线和地图的道路是存在偏移的,这个由于坐标系不统一造成的
let point = mars3d.PointTrans.gcj2wgs([longitude, latitude]);
//new Cesium.Cartographic(longitude, latitude, height),高度这里调成0
const cart = Cesium.Cartesian3.fromDegrees(point[0], point[1], 0.0);
这样调整之后线就完全在道路上了
🍥北京公交汽车路线和停车点
const actualBus = () => {
//处理数据
busRoute.map(item=>{
//绘制公交路线
let positions=item.polyline.split(';')
let lineData={
positions,
style: {
width: 4,
color: item.linecolor,
},
}
const BusLine = new mars3d.graphic.PolylinePrimitive(lineData);
graphicLayer.addGraphic(BusLine);
//绘制公交停车点
item.busstops.map(item=>{
const BusPoints = new mars3d.graphic.PointPrimitive({
position:item.location,
style:{
color: "#fff",
pixelSize: 5,
label: {
text: item.name,
font_size: 12,
color: "#19d5ff",
pixelOffsetY: -20,
background:true,
backgroundPadding:8,
distanceDisplayCondition: true,
distanceDisplayCondition_far: 5000,
distanceDisplayCondition_near: 0
}
}
})
graphicLayer.addGraphic(BusPoints);
})
})
};
首先说一下我这个公交线的数据来源高德地图
其中busstops是停车点位,name是公交路线名称,polyline是公交线的经纬度
使用
PolylinePrimitive图层画公交线,PointPrimitive图层画停车点,会发现点位是偏移的,这时候使用mars3d.PointTrans.gcj2wgs对坐标纠偏
🍙公交车行驶效果
这个参照了无人机的飞行轨迹的案例 看一下需要的数据结构
其中x是经度,y是纬度,z是海拔,time是经过该点的时间
首先获取一下当前的时间在toISOString一下 就得到了出发点的一组数据,紧接着对剩下的数据循环,通过Cesium.Cartesian3.distance计算两个点位的距离,在除以速度得到这个路段需要花费的时间,对前面的时间累加就得到给点经过时间
const getSampledPositionProperty = (points: any) => {
let speed = 20;
let time = 0;
let startTime = new Date();
// 公交车的行驶路径
let busPath = [
{
id: 1,
x: points[0].split(",")[0],
y: points[0].split(",")[1],
z: 0,
time: startTime.toISOString(),
},
];
// 公交车的发车时间
for (let i in points) {
if (i < points.length - 1) {
let pointFirst = points[i].split(",");
let pointSecond = points[Number(i) + 1].split(",");
let positionLeft = Cesium.Cartesian3.fromDegrees(pointFirst[0], pointFirst[1]);
let positionRight = Cesium.Cartesian3.fromDegrees(pointSecond[0], pointSecond[1]);
let distance = Cesium.Cartesian3.distance(positionLeft, positionRight);
time += (distance / 1000 / speed) * 3600;
let currentTime = parseInt(startTime.getTime() + time);
let BusMoment = new Date(currentTime).toISOString();
let vote = {
id: i + 2,
x: pointSecond[0],
y: pointSecond[1],
z: 0,
time: BusMoment,
};
busPath.push(vote);
}
}
const property = new Cesium.SampledPositionProperty();
property.forwardExtrapolationType = Cesium.ExtrapolationType.HOLD;
let start;
let stop;
for (let i = 0, len = busPath.length; i < len; i++) {
const item = busPath[i];
const lng = Number(item.x); // 经度
const lat = Number(item.y); // 纬度
const height = item.z; // 高度
const time = item.time; // 时间
let position = null;
if (lng && lat) {
position = Cesium.Cartesian3.fromDegrees(lng, lat, height);
}
let juliaDate = null;
if (time) {
juliaDate = Cesium.JulianDate.fromIso8601(time);
}
if (position && juliaDate) {
property.addSample(juliaDate, position);
}
if (i === 0) {
start = juliaDate;
} else if (i === len - 1) {
stop = juliaDate;
}
}
// 设置时钟属性
map.clock.startTime = start.clone();
map.clock.stopTime = stop.clone();
map.clock.currentTime = start.clone();
map.clock.clockRange = Cesium.ClockRange.LOOP_STOP;
map.clock.multiplier = 1;
if (map.controls.timeline) {
map.controls.timeline.zoomTo(start, stop);
}
// 创建path对象
let busPosition = new mars3d.graphic.PathEntity({
position: property,
orientation: new Cesium.VelocityOrientationProperty(property),
style: {
width: 0,//去除轨迹线
},
model: {
url: "/model/bus.gltf",
fill: true,
color: "#619e21",
scale: 1,
minimumPixelSize: 40,
},
});
graphicLayer.addGraphic(busPosition);
};
🍚给公交车增加车牌号和载客量
label: {
text: busRoute[0].licence + "(" + busRoute[0].passengers + "人)",
font_size: 12,
background: true,
backgroundColor: "#f7bd42",
outline: true,
outlineColor: "#fff",
hasPixelOffset: true,
pixelOffsetY: -20,
distanceDisplayCondition: true,
},
🍜给公交线增加起点和终点
对第一个点和最后一个增加带图片的标记点
if (index === 0) {
const startingPoint = new mars3d.graphic.BillboardPrimitive({
position: positions,
style: {
image: getAssetsFile("origin.png"),
scale: 0.7,
horizontalOrigin: Cesium.HorizontalOrigin.CENTER,
verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
},
});
graphicLayer.addGraphic(startingPoint);
} else if (index === monomer.busstops.length - 1) {
const endPoint = new mars3d.graphic.BillboardPrimitive({
position: positions,
style: {
image: getAssetsFile("end.png"),
scale: 0.7,
horizontalOrigin: Cesium.HorizontalOrigin.CENTER,
verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
},
});
graphicLayer.addGraphic(endPoint);
}
左侧栏目
新建一个CompositeIndex组件 做成的效果是
地图增加建筑模型和河流
shp文件处理
北京的建筑我放到这个文件夹下面了
我们要做的是把
shp文件转成一份 3dTiles数据集,可以使用cesiumlab
下载
大家自行注册一下,登陆成功后是这个样子
我们选择通用模型切片,在
输入文件中选择+shp,添加北京建筑.shp,下面列表会出现一行数据,点击右侧设置,
会出现一个设置的弹窗,在空间参考里选择从文件导入,下面截图的文件即可,造型参数,建筑高度选择成高度字段,点击确认即可,在数据存储选择输出路径点击提交就行,数据量大处理时间较长,请耐心等待
具体操作流程
添加加载北京市建筑物代码
// 加载北京市建筑物
const initBuilding = () => {
buildingLayer = new mars3d.layer.TilesetLayer({
name: "北京市建筑物",
url: "/model/building/tileset.json",
});
map.addLayer(buildingLayer);
};
效果图
增加河流
公交实时进度
坐公交的时候,一般都会看一下公交还有多久到站,避免错过,下面是车来了APP,我们也做一个差不多的,能够跟地图的公交位置进行同步。
获取公交实时位置
使用的是笑园实时公交API
获取一下北京市城市ID
import { onMounted } from "vue";
import { getBus } from "@/api";
// 获取城市列表
const getCityLists = async () => {
const res = await getBus({ optype: "city", uname: "2105856317@qq.com" });
console.log("城市列表", res);
};
onMounted(() => {
getCityLists();
});
获取公交路线
const getBusLine = async () => {
const res = await getBus({
optype: "luxian",
uname: "2105856317@qq.com",
cityid: "73",
keywords: "101",
keySecret: md5("2105856317@qq.com你的key值luxian"),
});
console.log("获取公交路线", res);
};
获取公交实时位置(2024/06/12更新)
const getBusPositon = async () => {
const res = await getBus({
optype: "rtbus",
uname: "2105856317@qq.com",
cityid: "73",
bus_linestrid: "MTEwMTAwMDU1NjYz",
bus_staname: "101",
keySecret: md5("2105856317@qq.com你的key值rtbus"),
});
};
这里着重看一下buses字段
"buses": [{
"lating": "39.9226288809075",
"longing": "116.3773169199641",
"distance": 368,
"dis_stat": 17
}, {
"lating": "39.9224136605734",
"longing": "116.39070257589405",
"distance": 143,
"dis_stat": 15
}, {
"lating": "39.924424558757735",
"longing": "116.41777588814439",
"distance": 391,
"dis_stat": 12
}, {
"lating": "39.919724",
"longing": "116.460417",
"distance": 0,
"dis_stat": 4
}]
定时请求这个接口,当distance等于0时,车在第dis_stat站,当distance不等于0时应该为第dis_stat-1站到dis_stat站的途中,这里简化了,就当做distance不等于0时,车在第dis_stat-1站,这样我们就轻松获取了101路公交车实时到达的公交站点,接下来做个样式。
公交实时进度样式
代码文件为:BusProgress.vue
模拟数据实现公交实时进度
const getBusPositon = () => {
intervalId = setInterval(function () {
++pageData.time;
console.log(pageData.time);
const res = station(pageData.time);
console.log(res);
for (let i in res) {
if (Number(res[i].distance) === 0) {
pageData.busIndex[i] = res[i].dis_stat;
} else {
pageData.busIndex[i] = res[i].dis_stat - 1;
}
}
}, 1000);
};
增加选中公交线地图公交线动画效果
let lineData = {
positions,
style: {
width: 4,
materialType: mars3d.MaterialType.LineFlow,
materialOptions: {
color: Cesium.Color.CHARTREUSE,
image: getAssetsFile("line-color-yellow.png"),
speed: 15,
},
},
};
const BusLine = new mars3d.graphic.PolylinePrimitive(lineData);