公司要做智慧消防楼层可视化,需要用到web3d,开源的引擎中先研究了cesium三维地球,但cesium做楼层感觉是大材小用,而且体验也不好,最终选用的是功能强大、更适合小型场景的three。

three是图形引擎,而web二维三维地图都是基于图形引擎的,所以拿three来开发需求简单的三维地图应用是没什么问题的。

1.坐标转换

实际地理坐标为经度、纬度、高度,而three.js使用的是右手坐标系x、y、z,本来考虑的是将经纬度坐标转换成墨卡托,再去和three的坐标系对应。而实际项目中,经纬度转墨卡托后,墨卡托的值太大,对应到three坐标系中,坐标距离原点太远,用户交互后,会有精度损失,于是先定义一个中间点,然后将墨卡托的结果减去这个中间点的值。(我自己是经度对应z轴,纬度对应x轴,高度对应y轴)

  1. function lonlatToMercator(lon,lat,height){
  2. var z = height ? height:;
  3. var x = (lon / 180.0) * 20037508.3427892;
  4. var y = (Math.PI / 180.0) * lat;
  5. var tmp = Math.PI / 4.0 + y / 2.0;
  6. y = 20037508.3427892 * Math.log(Math.tan(tmp)) / Math.PI;
  7. return {x: x,y: y,z: z};
  8. }
  9. var center = lonlatToMercator(lonVal,latVal,heightVal);
  1. function lonlatToThree(lon,lat,height){
  2. var z = height? height:;
  3. var x = (lon / 180.0) * 20037508.3427892;
  4. var y = (Math.PI / 180.0) * lat;
  5. var tmp = Math.PI / 4.0 + y / 2.0;
  6. y = 20037508.3427892 * Math.log(Math.tan(tmp)) / Math.PI;
  7. var result = {
  8. x: x - center.x,
  9. y: y - center.y,
  10. z: z -center.z
  11. };
  12. return result;
  13. }

2.加载模型

three.js支持多种模型加载,我是用草图大师建的模型,于是直接转成collada模型,然后使用three的collada模型加载器加载模型。因为要和three.js对应,而模型默认位于x-z轴上,所以要进行模型翻转等操作。

3.创建标注

three中,创建始终朝向相机的POI标注可以使用Sprite类,也可以使用canvas创建图标+文字类型的图形作为Sprite的纹理。sprite默认是有一个固定的3d长度,相机距离sprite越近,sprite在屏幕上越大,反之越小,过大或者过小都会导致sprite的canvas失真模糊,解决方案是计算出该点的屏幕像素与3d坐标长度的比值,然后将sprite缩放到一个合适的3d长度。

  1. var position = sprite.position;
  2. var canvas = sprite.material.map.image;
  3. if(canvas){
  4. var poiRect = {w:canvas.width,h:canvas.height};
  5. var scale = getPoiScale(position,poiRect);
  6. sprite.scale.set(scale[],scale[],1.0);
  7. }
  8.  
  9. function getPoiScale(position,poiRect){
  10. if(!position) return;
  11. var distance = camera.position.distanceTo(position);
  12. var top = Math.tan(camera.fov / * Math.PI / )*distance; //camera.fov 相机的拍摄角度
  13. var meterPerPixel = *top/container.clientHeight;
  14. var scaleX = poiRect.w * meterPerPixel;
  15. var scaleY = poiRect.h * meterPerPixel;
  16. return [scaleX,scaleY,1.0];
  17. }

4.标注碰撞

创建标注之后,放缩时难免会出现标注相互遮盖的情况,这样既影响美观也会遮盖住地图信息,这里需要检测标注间的遮盖,显示和不显示一些标注。

这里主要是将标注点3d坐标转成屏幕坐标,再根据sprite中canvas的长度和高度,就可以知道sprite在屏幕的矩形范围。接下来就是计算各个标注点sprite的矩形相交了。

  1. var sprite1 = {x:x1,y:y1,w:w1,h:h1}; //sprite1左下角x,y,宽度、高度
  2. var sprite2 = {x:x2,y:y2,w:w2,h:h2}; //sprite2左下角x,y,宽度、高度
  3. //检测两个标注sprite是否碰撞
  4. function isPOIRect(sprite1,sprite2){
  5. var x1 = sprite1.x,y1=sprite1.y,w1=sprite1.w,h1=sprite1.h;
  6. var x2 = sprite2.x,y2=sprite2.y,w1=sprite2.w,h1=sprite2.h;
  7. if (x1 >= x2 && x1 >= x2 + w2) {
  8. return false;
  9. } else if (x1 <= x2 && x1 + w1 <= x2) {
  10. return false;
  11. } else if (y1 >= y2 && y1 >= y2 + h2) {
  12. return false;
  13. } else if (y1 <= y2 && y1 + h1 <= y2) {
  14. return false;
  15. }else{
  16. return true;
  17. }
  18. }

5.加载设备

创建设备,我同样使用的是Sprite类,跟创建标注类似,放缩之后,sprite在屏幕上的大小保持不变。

6.设备点击

raycaster类用于在3d中被鼠标选中的物体,这同样可以选中sprite对象,于是用此方法模拟设备的点击。其中deviceGroup是保存所有设备sprite的object3d对象。

  1. function onDocumentMouseDown(e) {
  2. e.preventDefault();
  3. mouse.x = (e.clientX / window.innerWidth) * - ;
  4. mouse.y = -(e.clientY / window.innerHeight) * + ;
  5. //新建一个三维单位向量 假设z方向就是0.5
  6. //根据照相机,把这个向量转换到视点坐标系
  7. var vector = new THREE.Vector3(mouse.x, mouse.y,0.5).unproject(camera);
  8. //在视点坐标系中形成射线,射线的起点向量是照相机, 射线的方向向量是照相机到点击的点,这个向量应该归一标准化。
  9. var raycaster = new THREE.Raycaster(camera.position, vector.sub(camera.position).normalize());
  10. //射线和模型求交,选中一系列直线
  11. var intersects = raycaster.intersectObjects([deviceGroup],true);
  12. if (intersects.length > ) {
  13. var intersected = intersects[].object;
  14. if(intersected instanceof THREE.Sprite){
  15. //点击到设备图标
  16. }
  17. }
  18. }

7.弹出框

设备点击之后,一般都会以弹出框形式展示设备的具体信息,这里需要先定义弹出框的样式,然后将弹出点设备的三维坐标转换成屏幕坐标,设置一定的偏移量,再将弹出框放到偏移后的屏幕位置上。然后每次更改相机,重新计算弹出框的位置。

  1. //three世界坐标转为屏幕坐标
  2. function threeToScreen(position,camera){
  3. var worldVector = new THREE.Vector3(
  4. position.x,
  5. position.y,
  6. position.z
  7. );
  8. var standardVector = worldVector.project(camera);//世界坐标转标准设备坐标
  9. var a = window.innerWidth / ;
  10. var b = window.innerHeight / ;
  11. var x = Math.round(standardVector.x * a + a);//标准设备坐标转屏幕坐标
  12. var y = Math.round(-standardVector.y * b + b);//标准设备坐标转屏幕坐标
  13. return {
  14. x: x,
  15. y: y
  16. };
  17. }

8.设备动画

简单设备动画可以通过更改设备的材质、大小、位置来实现,比如通过定时更改设备的材质来实现设备图标的闪烁。

项目中要模拟火情,因此花了些时间网上参考并用粒子系统做了个火焰动画,这里先用一个循环通过THREE.Vector3对象创建构成火焰的全部的点,放到THREE.Geometry对象的vertices中;再使用canvas创建火焰的纹理图形,传给THREE.PointsMaterial对象(并设置材质透明transparent:true和加法混合THREE.AddictiveBlending),最后以前面的THREE.Geometry和THREE.PointsMaterial创建THREE.Points对象,完成该火焰粒子系统的初始化。

每个粒子都有单独的坐标,最后用一定的规律驱动粒子的移动达到动画的效果。

9.鼠标绘制

在3d中,鼠标的位置对应到三维坐标中是一条射线,因此需要添加绘制平面,点击时获取鼠标和绘制平面的交点,作为绘制点。绘制时监听鼠标的单击和移动事件。

绘制线时,鼠标点击和移动时,直接更改线的geometry中的vertices;绘制面时,不仅仅要更改vertices还要计算所有顶点组合的三角面(我使用的是Earcut.js),作为geometry的faces,最后创建一个以这个geometry为几何形状的多边形mesh。

  1. //positions 三维坐标数组[[x,y,z],[x,y,z],...]
  2. function createPolygon(positions){
  3. var shapePositons = [];
  4. for(var i=;i<positions.length;i++){
  5. var position = positions[i];
  6. shapePositons.push(new THREE.Vector3(position[],position[],position[]));
  7. }
  8. var data = [];
  9. for(var i=;i<positions.length;i++){
  10. var position = positions[i];
  11. data.push(position[],position[]);
  12. }
  13. var faces = [];
  14. var triangles = Earcut.triangulate(data);
  15. if(triangles && triangles.length != ){
  16. for(var i=;i<triangles.length;i++){
  17. var length = triangles.length;
  18. if(i%== && i < length-){
  19. faces.push(new THREE.Face3(triangles[i],triangles[i+],triangles[i+]));
  20. }
  21. }
  22. }
  23. var geometry = new THREE.BufferGeometry();
  24. geometry.vertices = shapePositons;
  25. geometry.faces = faces;
  26.  
  27. var mesh = new THREE.Mesh(geometry,material);
  28. return mesh;
  29. }

用three.js开发三维地图实例的更多相关文章

  1. 使用ESMap的地图平台开发三维地图

      本文简单的介绍使用ESmap的SDK开发(DIY自己地图的)一个地图的过程.若有不足,欢迎指正. 一.创建地图 只需四步,从无到有,在浏览器中创建一个自己的三维地图,炫酷到爆! 第一步:引入ESM ...

  2. 使用three.js开发3d地图初探

    three是图形引擎,而web二维三维地图都是基于图形引擎的,所以拿three来开发需求简单的三维地图应用是没什么问题的. 1.坐标转换 实际地理坐标为经度.纬度.高度,而three.js使用的是右手 ...

  3. Three.js实现3D地图实例分享

    本文主要给大家介绍了关于利用Three.js开发实现3D地图的实践过程,文中通过示例代码介绍的非常详细,对大家学习或者使用three.js具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习 ...

  4. IClient for js开发之地图的加载

    进行web开发之前首先需要安装IServer以及iClient for JavaScript的开发包.在这两中都具备的前提下进行第一步,如何调用IServer中发布的服务 调用iServer 中发布的 ...

  5. 如何使用JS来开发室内三维地图的轨迹回放功能

     在制作完成室内三维地图的功能后,最经常有的需求就是如何做人员的轨迹回放,一般流程都是从数据库中查询轨迹坐标后,经过后台查询接口返回给前端,接下来的事情都交给JS来完成. 如果想做好一个性能好的轨迹回 ...

  6. 【三维地图】开发攻略 —— 详解“GeoJSON”技术和应用场景

    GeoJSON ,一个用于存储地理信息的数据格式.GoeJSON对象可以表示几何.特征或特征集合,支持:点.线.面.多点.多线.多面和几何集合.在基于平面地图,三维地图中都需要用到的一种数据类型. 由 ...

  7. 百度地图 api 功能封装类 (ZMap.js) 本地搜索,范围查找实例

    百度地图 api 功能封装类 (ZMap.js) 本地搜索,范围查找实例 相关说明 1. 界面查看: 吐槽贴:百度地图 api 封装 的实用功能 [源码下载] 2. 功能说明: 百度地图整合功能分享修 ...

  8. SkylineGlobe 7.0.1 & 7.0.2版本Web开发 如何正确使用三维地图控件和工程树控件

    Skyline TerraExplorer Pro目前正式发布的7.0.1&7.0.2版本,还只是64位的版本, 在Web开发的时候,如何在页面中正确嵌入三维地图控件,让一些小伙伴凌乱了. 下 ...

  9. Javascript实战开发:教你使用raphael.js绘制中国地图

    最近的数据统计项目中要用到中国地图,也就是在地图上动态的显示某个时间段某个省份地区的统计数据,我们不需要flash,仅仅依靠raphael.js以及SVG图像就可以完成地图的交互操作.在本文中,我给大 ...

随机推荐

  1. red hat重置密码

    步骤1:打开red hat 步骤2:看到如图画面时按e 进入到这个界面 步骤4:按e,看到如下画面后,选第二项,然后按e 步骤5:在“quiet"后面输入   空格single   后按b ...

  2. LoadRunner录制HTTPS协议脚本

    学习LoadRunner录制HTTPS协议脚本,其实是一个意外的收获.当我拿到要测试的URL时,我像以前的步骤一样录制脚本,但是录制结束后,发现并没有生成脚本,开始以为是LoadRunner的原因,我 ...

  3. SCF(SenparcCoreFramework) 系列教程(一):项目介绍及快速搭建

    2020年3月25日的“盛派周三分享”活动首次使用直播的方式与大家见面,共有 500 多人参与了活动,得到了众多开发者的好评,并强烈要求我分享 PPT,这点要求当然必须满足啦! 除此以外,还有许多开发 ...

  4. 在TensorFlow中实现文本分类的卷积神经网络

    在TensorFlow中实现文本分类的卷积神经网络 Github提供了完整的代码: https://github.com/dennybritz/cnn-text-classification-tf 在 ...

  5. 骑士cms-通读全文-代码审计

    版本号:3.5.1 下载地址:http://103.45.101.75:66/2/201412/74cms.rar 1.审计方法 通读审计 1.1查看文件结构 首先需要看看有哪些文件和文件夹,寻找名称 ...

  6. IOS 常用的宏定义(#define)

    开发中经常用到的常量定义(随时更行): 与UIView相关 //获取View的frame属性 #define GetViewWidth(view) view.frame.size.width #def ...

  7. Nutch & Solr & IKAnalyzer Deployment Debug

    Nutch配置错误 (1) IO错误   记得出错的时候把出错的目录删除,否则下次还是出错. (2) Command crawl is deprecated, please use bin/crawl ...

  8. 各种杂记关于Linux

    修改Linux 日期 修改Linux时间

  9. go语言学习基础-编译文件

    1.创建工程在go的src目录下,比如我的go目录为/Users/yinxin/go,我创建文件夹 test路径为/Users/yinxin/go/src/test; 2.创建文件 main.go , ...

  10. A 组队参赛

    时间限制 : - MS   空间限制 : - KB  评测说明 : 1s,256m 问题描述 一年一度的ioiAKer大赛即将来临,何老板打算让信竞队的同学们组队参赛.信竞队共n名队员,他们的CF积分 ...