# 核心对象
以下我们将对vis3d.js中包含的各个类的使用中的关键点进行说明,同时也会阐释各个类之间的关系,各个类的具体属性及方法请查阅:api开发文档 (opens new window)。
注:vis3d.js中的对象都需要通过 vis3d. 开头来进行调用,如调用量算对象:vis3d.measure,调用标绘对象vis3d.plost。
# 构建地图viewer(MapViewer (opens new window))
在创建地图对象的过程中,我们常用以下方式构建地图对象:
let mapViewer = (window.mapViewer = new vis3d.MapViewer(
"mapContainer",
mapConfig
));
let viewer = mapViewer._viewer;
其中 mapViewer._viewer即为原生Cesium.Viewerd对象。
mapConfig属性说明:详细说明
# 公共方法(util (opens new window))
在开发过程中,我们经常要重复使用到各种方法,此对象内部封装了多种常用方法。 如:cartesianToLnglat(世界坐标转经纬度)、computeArea(计算面积)、getCameraView(获取相机姿态)......
# 小工具
框架内置了许多小工具,方便大家在开发使用。
# 指北针及比例尺
可自定义指北针位置及初始化视角等属性。
let compassTool = new Navigation(viewer, {
enableCompass: true, // 罗盘
/* compass: { // 罗盘位置设置
style: {
top: "120px",
left: "120px"
}
}, */
enableZoomControls: true, // 缩放控制器
enableDistanceLegend: true, // 比例尺
/* distanceLegend: { // 比例尺位置
style: {
top: "120px",
left: "120px"
}
}, */
enableCompassOuterRing: true, // 罗盘外环
view: {
"x": 117.16651563768625,
"y": 31.826203338964064,
"z": 1652.63605234587,
"heading": 88.01757556163417,
"pitch": -32.44506291616423,
"roll": 0.0015492995898727518,
"duration": 0
} // 初始化视角
});
# 信息提示框
展示鼠标当前位置以及相机当前姿态。
let lnglatNavigation = new LatlngNavigation(viewer);
# 右键菜单
点击鼠标右键时,弹出操作菜单。
let rightTool = new RightTool(this.viewer, {});
# 创建图层(layer)
我们在框架内封装了多个服务加载类,只需要进行简单的配置,即可加载对应的地图服务。
# 普通加载
普通加载有以下两种方式:
- (推荐)使用new layer.Tool(),通过此对象add方法,传入不同type类型,进行不同图层的创建。
let layerTool = new vis3d.layer.Tool(viewer);
layerTool.add({
type: "mapserver",
iconImg: "./vis3d/images/baseMap/geoq.png",
url: "http://map.geoq.cn/arcgis/rest/services/ChinaOnlineStreetPurplishBlue/MapServer"
})
type类型有:xyz、wfs、geojson、mapserver、arcgiscache、tdt、singleImage、tms、3dtiles、wms、wmts、grid、tencent、baidu、osm、urltemplate。
说明:layerTool.add()方法实际上与第2种方式原理是一致的。
- 通过vis3d.layer下的各个图层类来创建图层对象。 以下为layer所包含的图层类型:
图层服务类型 | 说明 |
TilesetLayer | 3dtiles模型 |
MapserverLayer | arcmap处理的规范切片 |
ArcgiscacheLayer | arcgis标准mapserver服务 |
GridLayer | 网格类型 |
GeojsonLayer | geojson类型数据 |
TDTLayer | 天地图 |
SingleImageLayer | 单张图片 |
TMSLayer | tms类型服务 |
XYZLayer | xyz格式切片 |
WMSLayer | ogc wms服务 |
WMTSLayer | ogc wmts服务 |
TencentLayer | 腾讯地图 |
BaiduLayer | 百度地图 |
osmLayer | osm服务 |
UrltemplateLayer | cesium urltemplate服务 |
let layerObj = new vis3d.layer.ArcgiscacheLayer(viewer, opt);
layerObj.load();
说明:第1种和第2种方式相比,可创建一个layer.Tool对象后,通过该对象的load()方法重复多次加载不同类型的服务,并且用该对象对这些服务进行管理,所以推荐第1种。
# 快速加载
大多时候,我们不希望写繁琐的代码加载单个服务,此时就可以通过vis3d.layerload.add()方法来创建图层对象、加载地图服务。
vis3d.layerload.add({
url: "http://mapgl.com/data/model/qx-simiao/tileset.json",
maximumScreenSpaceError: 1,
type: "3dtiles",
center: {
z: 120
},
flyTo: false
}, viewer);
此方法一般用于快速加载某类型数据。
# 标绘相关(plot)
vis3d中支持多种类型的标绘,并且针对标绘的各个阶段(如:开始标绘、开始编辑等)都加上了监听。仅需要如下创建对象即可:
let plotDrawTool = new vis3d.plot.Tool(window.viewer, {
canEdit: true,
});
plotDrawTool.on("endCreate", function (entObj, ent) {
// 创建完成后
});
plotDrawTool.on("startEdit", function (entObj, ent) {
// 开始编辑
});
plotDrawTool.on("endEdit", function (entObj, ent) {
// 编辑完成后
});
下面我们就可以通过plotDrawTool来进行不同类型的标绘了。
# 普通标绘
vis3d中,我们可以快速进行点、线、面、模型以及其他多种类型对象的绘制。 目前支持的通用类型有:
标绘类型 | 说明 |
point | 点 |
polyline | 线 |
polygon | 面 |
billboard | 图标 |
circle | 圆 |
rectangle | 矩形 |
gltfModel | gltf/glb小模型 |
plotDrawTool.start({
"type": "polyline", // 线标绘
"style": { // 样式
"clampToGround": true,
"color": "#ffff00"
}
});
# 军事标绘
并且在标绘中,也支持常见的军事类型标绘。
包含:1-攻击箭头、2-攻击箭头(平尾)、3-攻击箭头(燕尾)、4-闭合曲面、5-钳击箭头、6-单尖直箭头、7-粗单尖直箭头(带燕尾)、8-集结地、9-弓形面、10-直箭头、11-矩形旗、12-扇形、13-三角旗、14-矩形波浪旗、17-多边形、18-圆形等。
plotDrawTool.start({
"type": "arrow", // 军事标绘
"arrowType" : 1, // 箭头类型
"style": { // 样式
"color": "#ddff00",
"heightReference": 1
}
});
# 快速标绘
通常情况下,我们需要快速进行标绘,此时可使用vis3d.draw。
vis3d.draw.start({
type: "circle",
style : {
color : "#ff0000",
colorAlpha:.1,
},
success(entObj: any) {
}
}, viewer);
# 图上量算(measure)
vis3d中,支持以下类型的测量:
1-空间距离测量、2-贴地距离测量、3-空间面积测量、4-高度测量、5-三角测量、6-坐标量算、7-方位角测量、8-剖面测量、9-单点坡度。
与标绘类似,也可以给绘制对象绑定不同的监听事件(startEdit 开始编辑时 / endEdit 编辑结束时 / endCreate 创建完成后)。
// 创建对象
let measureTool = new vis3d.measure.Tool(viewer);
measureTool.on("(startEdit", function (ms) {
// 开始编辑时
});
measureTool.on("endEdit", function (ms) {
// 编辑结束时
});
measureTool.on("endCreate", function (ms) {
// 创建完成后
});
// 开始量算
measureTool.start({
type: 1
});
# 模型相关(tileset)
在Cesium中,osgb、bim以及3dmax等模型通常会转为3dtiles格式,针对3dtiles模型我们可以做许多特效。以下针对模型的特效均基于Cesium1.103之后开发。
# 模型剖切
模型剖切的原理是使用了Cesium.ClippingPlane。目前支持按方向剖切以及按角度剖切。
- 按方向剖切
let modelClip = new vis3d.tileset.Clip(viewer,{
tileset : tileset, // 模型对象
dir : new Cesium.Cartesian3(0.0, 1.0, 0.0) , // 剖切面法向量方向
distance : 10 // 剖切距离
});
modelClip.start();
- 按角度剖切
let modelClip = new vis3d.tileset.Clip(viewer,{
tileset : tileset, // 模型对象
angle : 60 , // 剖切角度
distance : 10 // 剖切距离
});
modelClip.start();
# 模型编辑
针对模型的位置、大小、姿态进行编辑。
# 模型压平
# 模型剖切
# 空间分析
# 可视域分析
展示3dtiles模型上的可见区与不可见区域。
let visualFieldTool = new window.vis3d.analysis.VisualFieldTool(
viewer
);
visualFieldTool.startDraw({
horizontalFov: 120, // 水平张角
verticalFov: 60, // 垂直张角
distance: 0, // 距离
heading: 0, // 偏转角
pitch: 0, // 仰俯角
visibleAreaColor: "#00FF00", // 可视区域颜色
visibleAreaColorAlpha: 0.5, // 可视区域颜色透明度
hiddenAreaColor: "#FF0000", // 不可视区域颜色
hiddenAreaColorAlpha: 0.5 // 不可视区域颜色透明度
});
# 缓冲区分析
一般来说,缓冲区是和业务进行结合的,此处仅供参考。核心是利用利用了plot以及turf.js。
let bufferDrawTool = new window.vis3d.plot.Tool(viewer, {
canEdit: false,
});
// 1、点状缓冲区
const drawPoint = () => {
bufferDrawTool.start({
name: "图标",
type: "billboard",
style: {
image: markerimg,
},
styleType: "billboard",
success: function (entObj, ent) {
let lnglats = entObj.getPositions(true);
let turfObj = turf.point(lnglats);
let buffered = turf.buffer(turfObj, Number(radius.value) / 1000, {
units: "miles",
});
createPolygon(buffered);
},
});
}
// 2、线状缓冲区
const drawLine = () => {
bufferDrawTool.start({
type: "polyline",
styleType: "polyline",
style: {
clampToGround: true,
color: "#ffff00",
},
success: function (entObj, ent) {
let lnglats = entObj.getPositions(true);
let turfObj = turf.lineString(lnglats);
let buffered = turf.buffer(turfObj, Number(radius.value) / 1000, {
units: "miles",
});
createPolygon(buffered);
},
});
}
// 3、面状缓冲区
const darwArea = () => {
bufferDrawTool.start({
type: "polygon",
styleType: "polygon",
style: {
color: "#00FFFF",
colorAlpha: 0.5,
fill: false,
outline: true,
outlineColor: "#ff0000",
heightReference: 1,
},
success: function (entObj, ent) {
let lnglats = entObj.getPositions(true);
lnglats = lnglats.concat([lnglats[0]]);
let turfObj = turf.polygon([lnglats]);
let buffered = turf.buffer(turfObj, Number(radius.value) / 1000, {
units: "miles",
});
createPolygon(buffered);
},
});
}
// 构建缓冲区范围
const createPolygon = (positions){}
# 限高分析
let limitHeightDrawTool = new window.vis3d.plot.Tool(viewer, {
canEdit: false,
});
limitHeightDrawTool.on("endCreate", function (entObj: any, ent: any) {
const positions = entObj.getPositions();
limitHeightDrawTool.removeAll();
limitHeight = new window.vis3d.analysis.LimitHeight(viewer, {
positions: positions,
bottomHeight: Number(bottomHeight.value), // 底部高度
topHeight: Number(topHeight.value) // 顶部高度
});
});
# 方量分析
方量分析,原理是对面进行插值求和。纯前端中,求地形高度时,不通地图层级下的同一个点高度不同,所以此处会有较大的误差,若想精确求方量,建议结合原始tiff文件进行计算。
let drawTool = new vis3d.plot.Tool(viewer, {
canEdit: false,
});
drawTool.on("endCreate", function (entObj, ent) {
const positions = entObj.getPositions();
let uniformData = window.vis3d.util.computeUniforms(
positions,
false,
viewer
);
const minh = uniformData.minHeight; // 当前范围内的最低点高度
const maxh = uniformData.maxHeight; // 当前范围内的最高点高度
const digTotalV = digV(uniformData, minh); // 挖方量
const fillTotalV = fillV(uniformData, maxh); // 填方量
});
// 挖方体积
const digV = (data, minh) => {
if (!data) return;
let uniforms = data.uniformArr;
if (!uniforms || uniforms.length == 0 || !minh) return;
let totalV = 0;
for (let i = 0; i < uniforms.length; i++) {
let item = uniforms[i];
if (item.height > minh) {
let v = item.area * (item.height - minh);
totalV += v;
}
}
totalV = Number(totalV).toFixed(2);
return totalV;
}
// 填方体积
const fillV = (data, maxh) => {
if (!data) return;
let uniforms = data.uniformArr;
if (!uniforms || uniforms.length == 0 || !maxh) return;
let totalV = 0;
for (let i = 0; i < uniforms.length; i++) {
let item = uniforms[i];
if (maxh > item.height) {
let v = item.area * (maxh - item.height);
totalV += v;
}
}
totalV = Number(totalV).toFixed(2);
return totalV;
}
# 通视分析
通视分析分两种:1、基于地形的通视分析 2、基于3dtiles的通视分析
// 1、地形通视分析
let ctgc_center = Cesium.Cartographic.fromCartesian(centerPosition);
ctgc_center.height = ctgc_center.height + 2;
let center = Cesium.Cartographic.toCartesian(ctgc_center);
let ctgc_aim = Cesium.Cartographic.fromCartesian(aimPosition);
ctgc_aim.height = ctgc_aim.height + 1;
let aim = Cesium.Cartographic.toCartesian(ctgc_aim);
// 计算障碍点坐标
let point = window.vis3d.util.getIntersectPosition({
startPoint: center,
endPoint: aim,
},viewer);
// 2、模型通视分析 (模型通视误差较大)
......
# 坡度分析
......
# 日照分析
let sunshine = new window.vis3d.analysis.Sunshine(viewer, {
startTime: startDate,
endTime: endDate,
multiplier: 2400 // 播放速度
});
sunshine.start();
# 动态材质
此处动态材质分两种,一种是graphic的材质,一种是primitive的材质。
# 动态圆
let redEllipse = viewer.entities.add({
position: Cesium.Cartesian3.fromDegrees(117.50, 38.0),
ellipse: {
semiMinorAxis: 1500.0,
semiMajorAxis: 1500.0,
heightReference: 1,
material: new window.vis3d.material.EllipseWave({
duration: 2000,
color: Cesium.Color.RED,
}),
}
})
# 椎体扫描
let cylinder = viewer.entities.add({
position: Cesium.Cartesian3.fromDegrees(117, 32, 150000),
cylinder: {
length: 299800,
topRadius: 0.0,
bottomRadius: 30000,
material: new window.vis3d.material.CylinderScan({ //CircleFadeMaterial CircleFadeMaterial
duration: 2000,
color: Cesium.Color.RED,
}),
}
});
# 雷达椎体
此为primitive。
let length = 10000; // 椎体长度
// 地面位置(垂直地面)
let positionOnEllipsoid = Cesium.Cartesian3.fromDegrees(opt.x, opt.y, opt.z);
// 矩阵计算
let modelMatrix = Cesium.Matrix4.multiplyByTranslation(
Cesium.Transforms.eastNorthUpToFixedFrame(positionOnEllipsoid),
new Cesium.Cartesian3(0.0, 0.0, length * 0.5), new Cesium.Matrix4()
);
// 创建雷达放射波
let cylinderGeometry = new Cesium.CylinderGeometry({
length: length,
topRadius: 0.0,
bottomRadius: opt.bottomRadius || length * 0.5,
vertexFormat: Cesium.MaterialAppearance.MaterialSupport.TEXTURED.vertexFormat
});
// 创建GeometryInstance
let redCone = new Cesium.GeometryInstance({
geometry: cylinderGeometry,
modelMatrix: modelMatrix,
});
// 创建Primitive
let radar = viewer.scene.primitives.add(new Cesium.Primitive({
geometryInstances: [redCone],
appearance: new Cesium.MaterialAppearance({
// 自定义纹理
material: new Cesium.Material({
fabric: {
type: 'radarS',
uniforms: {
color: opt.color || new Cesium.Color(2, 1, 0.0, 0.8),
repeat: 30.0,
offset: 0.0,
thickness: 0.3,
},
source: `
uniform vec4 color;
uniform float repeat;
uniform float offset;
uniform float thickness;
czm_material czm_getMaterial(czm_materialInput materialInput)
{
czm_material material = czm_getDefaultMaterial(materialInput);
float sp = 1.0/repeat;
vec2 st = materialInput.st;
float dis = distance(st, vec2(0.5));
float m = mod(dis + offset, sp);
float a = step(sp*(1.0-thickness), m);
material.diffuse = color.rgb;
material.alpha = a * color.a;
return material;
}
`
},
translucent: false
}),
faceForward: false, // 当绘制的三角面片法向不能朝向视点时,自动翻转法向,从而避免法向计算后发黑等问题
closed: true // 是否为封闭体,实际上执行的是 是否进行背面裁剪
}),
}));
// 动态修改雷达材质中的offset变量,从而实现动态效果。
viewer.scene.preUpdate.addEventListener(function () {
let offset = radar.appearance.material.uniforms.offset;
offset -= 0.001;
if (offset > 1.0) offset = 0.0;
radar.appearance.material.uniforms.offset = offset;
});
# 动态球体
let ent = viewer.entities.add({
position: Cesium.Cartesian3.fromDegrees(117.297110, 31.814635, 29.86),
ellipsoid: {
radii: new Cesium.Cartesian3(1000.0, 1000.0, 1000.0),
material: new vis3d.material.Elipsoid({
color: new Cesium.Color(1.0, 1.0, 0.0, 1.0),
speed: 3
})
}
})
# 飞线、流动线
viewer.entities.add({
polyline: {
positions: positions,
width: 1,
material: new window.vis3d.material.LineFlow({
color: Cesium.Color.fromRandom(),
duration: 3000,
image: texture,
repeat: new Cesium.Cartesian2(1, 1) //平铺
})
}
});
# 光罩
let shader = `
uniform vec4 color;
uniform float speed;
#define pi 3.1415926535
#define PI2RAD 0.01745329252
#define TWO_PI (2. * PI)
float rands(float p){
return fract(sin(p) * 10000.0);
}
float noise(vec2 p){
float time = fract( czm_frameNumber * speed / 1000.0);
float t = time / 20000.0;
if(t > 1.0) t -= floor(t);
return rands(p.x * 14. + p.y * sin(t) * 0.5);
}
vec2 sw(vec2 p){
return vec2(floor(p.x), floor(p.y));
}
vec2 se(vec2 p){
return vec2(ceil(p.x), floor(p.y));
}
vec2 nw(vec2 p){
return vec2(floor(p.x), ceil(p.y));
}
vec2 ne(vec2 p){
return vec2(ceil(p.x), ceil(p.y));
}
float smoothNoise(vec2 p){
vec2 inter = smoothstep(0.0, 1.0, fract(p));
float s = mix(noise(sw(p)), noise(se(p)), inter.x);
float n = mix(noise(nw(p)), noise(ne(p)), inter.x);
return mix(s, n, inter.y);
}
float fbm(vec2 p){
float z = 2.0;
float rz = 0.0;
vec2 bp = p;
for(float i = 1.0; i < 6.0; i++){
rz += abs((smoothNoise(p) - 0.5)* 2.0) / z;
z *= 2.0;
p *= 2.0;
}
return rz;
}
czm_material czm_getMaterial(czm_materialInput materialInput)
{
czm_material material = czm_getDefaultMaterial(materialInput);
vec2 st = materialInput.st;
vec2 st2 = materialInput.st;
float time = fract( czm_frameNumber * speed / 1000.0);
if (st.t < 0.5) {
discard;
}
st *= 4.;
float rz = fbm(st);
st /= exp(mod( time * 2.0, pi));
rz *= pow(15., 0.9);
vec4 temp = vec4(0);
temp = mix( color / rz, vec4(color.rgb, 0.1), 0.2);
if (st2.s < 0.05) {
temp = mix(vec4(color.rgb, 0.1), temp, st2.s / 0.05);
}
if (st2.s > 0.95){
temp = mix(temp, vec4(color.rgb, 0.1), (st2.s - 0.95) / 0.05);
}
material.diffuse = temp.rgb;
material.alpha = temp.a * 2.0;
return material;
}
`;
Cesium.Material.EllipsoidElectricType = 'EllipsoidElectric';
Cesium.Material._materialCache.addMaterial(
Cesium.Material.EllipsoidElectricType,
{
fabric: {
type: Cesium.Material.EllipsoidElectricType,
uniforms: {
color: new Cesium.Color(1.0, 0.0, 0.0, 0.7),
speed: 1,
},
source: shader,
},
translucent: function (material) {
return true;
},
}
);
let op = viewer.scene.primitives.add(
new Cesium.Primitive({
geometryInstances: new Cesium.GeometryInstance({
geometry: new Cesium.EllipsoidGeometry({
radii: { x: 1000, y: 1000, z: 1000 },
maximumCone: Cesium.Math.PI_OVER_TWO,
}),
modelMatrix: Cesium.Transforms.eastNorthUpToFixedFrame(
Cesium.Cartesian3.fromDegrees(110.95115601835178, 34.93149218673128,100)
),
}),
appearance: new Cesium.MaterialAppearance({
material: Cesium.Material.fromType('EllipsoidElectric', {
color: Cesium.Color.GREEN,
speed: 5,
}),
}),
})
);
# 扫描圆
let position = Cesium.Cartesian3.fromDegrees(117.28251052606026, 31.72);
let entity = viewer.entities.add({
position: position,
ellipse: {
semiMinorAxis: 600.0,
semiMajorAxis: 600.0,
heightReference: 1,
material: new window.vis3d.material.EllipseScan({//多个圆圈
duration: 2000,//动画时长,单位:毫秒
color: Cesium.Color.YELLOW
}),
}
});
# 动态墙
viewer.entities.add({
wall: {
positions: positions1,
width: 5,
maximumHeights: maximumHeights1,
minimumHeights: minimumHeights1,
material: new window.vis3d.material.Wall({ //动画线材质
color: Cesium.Color.RED,
duration: 3000,
image: texture,
axisY: false,
repeat: new Cesium.Cartesian2(1, 1) //平铺
}),
}
});
← 项目使用