创建应用
创建3D应用
通过 vjmap3d 中的 App 来创建一个3d应用。App需要设置container与一个div容器相关联。
示例如下:
// 创建一个服务类,与服务连接
let svc = new vjmap3d.Service(env.serviceUrl, env.accessToken);
let app = new vjmap3d.App(svc, {
container: "map", // 容器id
// 渲染参数
render: {
alpha: true,
antialias: true
},
// 是否显示性能统计面板
stat: {
show: true,
left: "0"
},
// 场景设置
scene: {
// 是否显示坐标网格
gridHelper: {
visible: true
}
},
// 相机设置
camera: {
// 是否显示视角指示器
viewHelper: {
enable: true
}
}
})
提示
免费试用版不提供私有化部署,直接连官网的服务进行试用,有水印。如果需要正式版本私有化部署,请联系客服。
选择渲染引擎(WebGL / WebGPU)
vjmap3d v2 起支持 WebGPU 渲染引擎。创建应用时通过渲染参数 render.engine 选择:"webgl"(默认,WebGLRenderer)或 "webgpu"(WebGPURenderer)。浏览器不支持 WebGPU 时,three 的 WebGPURenderer 会自动降级为 WebGL2 后端(仍属 webgpu 引擎:app.renderer.isWebGPURenderer === true、仍走 TSL 材质,GLSL ShaderMaterial 依旧不可用);只有 three/webgpu/three/tsl 动态加载失败(如 importmap 缺映射)或渲染器 init() 抛错时,才回退到 webgl 引擎(WebGLRenderer)。
版本要求
webgpu 引擎与 app.whenReady() 均为 v2.0.0 起的 API——旧版 vjmap3d@1.0.23(webgl-only,peer three@^0.165)的 dist 中没有。1.x 用户请直接使用默认 webgl 引擎并跳过下例的 await app.whenReady()(webgl 渲染器同步就绪)。版本分界详见 WebGPU 渲染引擎。
let svc = new vjmap3d.Service(env.serviceUrl, env.accessToken);
let app = new vjmap3d.App(svc, {
container: "map",
render: {
engine: "webgpu", // 渲染引擎: 'webgl'(默认) 或 'webgpu'
// forceWebGL: true, // 可选: webgpu 引擎强制使用 WebGL2 后端(兼容不支持 WebGPU 的环境)
antialias: true
}
})
// webgpu 引擎的渲染器为异步初始化:访问 app.renderer 前先等待就绪
// (webgl 引擎下立即返回;v2.0.0 起提供,旧版 1.x 无此方法可省略)
await app.whenReady();
console.log(app.renderer.isWebGPURenderer
? (app.renderer.backend?.isWebGPUBackend ? "WebGPU" : "WebGL2 后端(WebGPURenderer)")
: "WebGL(webgl 引擎)");
提示
webgpu 引擎下,内置特效材质与后期处理(辉光/描边等)会自动切换为 TSL / WebGPU 实现,用法不变。详细说明(浏览器要求、回退规则、自定义材质注意事项)见 WebGPU 渲染引擎。
注意:vjmap3d 做为唯杰地图的三维图层使用时(见下文),因需共享 2D 地图的 WebGL 上下文,不支持 webgpu 引擎。
创建CAD为底图的3D应用
通过 vjmap3d 中的 MapViewEntity 来创建一个地图实体。
// 创建一个服务类,与服务连接
let svc = new vjmap3d.Service(env.serviceUrl, env.accessToken);
let app = new vjmap3d.App(svc, {
container: "map", // 容器id
camera: { // 相机设置
viewHelper: { enable: true } // 是否显示视角指示器
}
})
let res = await app.svc.openMap({
mapid: env.exampleMapId, // 地图ID,(请确保此ID已存在,可上传新图形新建ID)
mapopenway: vjmap3d.MapOpenWay.GeomRender, // 以几何数据渲染方式打开
style: vjmap3d.openMapDarkStyle()
});
if (res.error) {
showError(res.error);
return;
}
// 获取当前地图的可视范围
let clipPolygon = vjmap3d.GeoBounds.fromString(res.drawBounds).toPointArray();
let provider = new vjmap3d.MapProvider(
[
{
url: app.svc.rasterTileUrl()
}
],
{
transparent: true
}
);
let mapBounds = vjmap3d.GeoBounds.fromString(res.bounds)
let scale = 100;
let mapviewEnt = new vjmap3d.MapViewEntity({
provider,
baseScale: scale,
mapBounds: mapBounds.toArray(),
clipPolygon // 如果透明的话,不能使用裁剪区载
});
mapviewEnt.addTo(app);
//
// 在CAD图中心上方创建一个Mesh
let cadLen = mapBounds.width() / 50; // 取cad图长度的1/50
// toDist 的 app 形参默认值是"当前全局 App"(new App 之后才有)——必须在 App 创建后调用,否则崩溃
let len = vjmap3d.toDist(cadLen); // cad长度转世界坐标长度
let box = new THREE.BoxGeometry(len, len, len)
const material = new THREE.MeshStandardMaterial({
color: "#f00",
side: THREE.DoubleSide
});
let mesh = new THREE.Mesh(box, material);
let cadPos = [mapBounds.center().x, mapBounds.center().y, cadLen]; // mesh在cad中坐标
//
let worldPos = vjmap3d.toWorld(cadPos);// cad坐标转世界坐标
mesh.position.copy(worldPos);
//app.scene.add(mesh) // 可以直接把 mesh 加进场景中不与实体相关联
// 创建一个实体与mesh相关联
let entity = vjmap3d.Entity.fromObject3d(mesh);
entity.addTo(app);
// 增加一个事件模块,用来高亮提示
entity.add(vjmap3d.EventModule, {
hoverSelect: true,
hoverHtmlText: '我是用vjmap3d创建的Box',
popupOptions: {
anchor: "bottom"
}
});
做为唯杰地图的三维图层
vjmap3d既可做为一个单独的3D引擎, 也可以基于唯杰地图VJMAP做为一个3D图层,能够在保留二维图形信息的直观性同时,融入三维空间的深度感知和交互性,为用户带来更丰富、立体和沉浸式的视觉体验及数据分析能力。
其步骤为:
- 引入
vjmapsdk,创建vjmap.Map地图对象,具体示例可查看 https://vjmap.com/demo - 创建 MapThreeLayer 3d图层
- 通过
map.addLayer增加3d图层,通过3d图层获取App对象。
示例如下:
let svc = new vjmap.Service(env.serviceUrl, env.accessToken)
// 打开地图
let res = await svc.openMap({
mapid: env.exampleMapId, // 地图ID
// @ts-ignore
mapopenway: vjmap.MapOpenWay.GeomRender, // 以几何数据渲染方式打开
style: vjmap.openMapLightStyle() // div为深色背景颜色时,这里也传深色背景样式
})
if (res.error) {
// 如果打开出错
showError(res.error)
return;
}
// 获取地图范围
let mapBounds = vjmap.GeoBounds.fromString(res.bounds);
// 根据地图范围建立几何投影坐标系
let prj = new vjmap.GeoProjection(mapBounds);
//
// 地图对象
let map = new vjmap.Map({
container: 'map', // DIV容器ID
style: svc.rasterStyle(), // 样式,这里是栅格样式
center: prj.toLngLat(mapBounds.center()), // 设置地图中心点
zoom: 2, // 设置地图缩放级别
pitch: 45,
antialias: true, // 反锯齿
renderWorldCopies: false // 不显示多屏地图
});
//
// 关联服务对象和投影对象
map.attach(svc, prj);
await map.onLoad();
//
// 创建3d图层
let mapLayer = new vjmap3d.MapThreeLayer(map, {
stat: {
show: true,
left: "0"
}
});
map.addLayer(new vjmap.ThreeLayer({ context: mapLayer }))
let app = mapLayer.app;
//
// 在CAD图中心上方创建一个Mesh
let cadLen = mapBounds.width() / 50; // 取cad图长度的1/50
// toDist 的 app 形参默认值是"当前全局 App"(new App 之后才有)——必须在 App 创建后调用,否则崩溃
let len = vjmap3d.toDist(cadLen); // cad长度转世界坐标长度
let box = new THREE.BoxGeometry(len, len, len)
const material = new THREE.MeshStandardMaterial({
color: "#f00",
side: THREE.DoubleSide
});
let mesh = new THREE.Mesh(box, material);
let cadPos = [mapBounds.center().x, mapBounds.center().y, cadLen]; // mesh在cad中坐标
//
let worldPos = vjmap3d.toWorld(cadPos);// cad坐标转世界坐标
mesh.position.copy(worldPos);
//app.scene.add(mesh) // 可以直接把 mesh 加进场景中不与实体相关联
// 创建一个实体与mesh相关联
let entity = vjmap3d.Entity.fromObject3d(mesh);
entity.addTo(app);
// 增加一个事件模块,用来高亮提示
entity.add(vjmap3d.EventModule, {
hoverSelect: true,
hoverHtmlText: '我是用vjmap3d创建的Box',
popupOptions: {
anchor: "bottom"
}
});
总结
使用vjmap3d创建3d应用 和 做为唯杰地图的三维图层 使用,除了初始化代码不同外,业务代码基本上可复用,做到同一套代码用在不同的框架上。但要注意坐标轴差异:独立 App 为 Y 轴向上,三维图层模式(app.isMapMode)为 Z 轴向上(高度在 .z、地面为 XY 平面、旋转轴用 (0,0,1)),涉及高度/朝向的代码需按 app.isMapMode 分支,详见 坐标转换。
需要说明的是:当vjmap3d做为唯杰地图的三维图层使用时,其相机是由唯杰地图控制的,vjmap3d的相机会和唯杰地图相机保持同步。因此在这种模式下,不能直接操作vjmap3d的相机。同时因为唯杰地图暂不支持后期处理,所以在这种模式下,vjmap3d的后期处理功能会自动禁用。