WebGL+ArcGIS JS API实现Web城市地下管线三维场景浏览

2024-02-19 19:32

本文主要是介绍WebGL+ArcGIS JS API实现Web城市地下管线三维场景浏览,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

注:转载请注明出处

WebGL发展的如火如荼,未来的WebGIS也应该体现3D的趋势,本人的本科毕业论文是《Web城市地下管线三维场景浏览技术研究》,通过ArcGIS JS API获取地理数据,然后用WebGL框架Three.js将该地理数据进行三维展示。

本论文主要具有以下创新点:

(1)在传统的WebGIS的基础上,借助于主流的IT技术(WebGL)实现了在传统网页中嵌入三维GIS模块,并且可以在网页中导入三维模型,使得用户可以通过网页流畅的查看地下管线的详细信息,拓展了传统WebGIS的应用价值。

(2)本系统才用了WebGL开发技术用于创建三维场景,WebGL是GPU硬件加速的,降低了CPU的压力,因此三维体验更加流畅。并且浏览器是原生支持WebGL,不需要安装任何插件。

(3)在三维场景中可以实现三维场景的漫游(平移、缩放、旋转),并且还能够在三维场景中进行鼠标交互式查询。


虽然已经在网页中实现了管网的三维可视化,并在此基础上实现了三维漫游与交互式查询,但是系统还存在一些不足:

(1)没有实现专业的三维管线分析功能,这使得系统的空间分析功能受到了限制。

(2)并且系统的三维场景不能完全容纳整个二维地图中的所有管线与管点,每次都需要鼠标在二维地图中选择要创建三维管网的区域,比较麻烦。

为了解决上述问题,今后需要进行以下几方面的研究:

(1)由于本论文已经在三维场景中实现了三维对象的拾取功能,并且每个三维对象中都保存了对应的地理空间数据,所以可以利用三维对象的拾取功能在三维管线分析中进行鼠标交互式处理,并且根据三维对象保存的地理空间数据进行空间分析,将分析的结果再以三维对象的形式显示出来。以三维对象作为显示层,以三维对象中的地理空间数据作为数据分析层,将二者结合起来就可以进行多种专业的三维管线分析。

(2)由于系统的三维场景不能完全容纳整个二维地图中的所有管线与管点,所以需要研究算法实现通过键盘方向键进行三维场景动态内容的更新。


以下是系统截图,左侧为选定的二维区域,右侧为动态生成的对应的三维管网场景:


以下是三维单击查询截图:


以下是主要源码:

<!DOCTYPE HTML>
<html>
<head><title></title><meta http-equiv="Content-Type" content="text/html" /><meta name="charset" content="utf-8"/><!--<link rel="stylesheet" type="text/css" href="http://serverapi.arcgisonline.com/jsapi/arcgis/2.8/js/dojo/dijit/themes/claro/claro.css">--><link rel="stylesheet" type="text/css" href="http://localhost/arcgis_js_api/library/2.7/jsapi/js/dojo/dijit/themes/claro/claro.css"><style type="text/css">/*@import "http://serverapi.arcgisonline.com/jsapi/arcgis/2.8/js/dojo/dijit/themes/claro/claro.css";*/@import "http://localhost/arcgis_js_api/library/2.7/jsapi/js/dojo/dijit/themes/claro/claro.css";@import "iSpring/widgets/themes/MapToolbar.css";@import "iSpring/widgets/themes/PipeToolbar.css";@import "iSpring/widgets/themes/PipeTOC.css";@import "iSpring/widgets/themes/PipeIdentify.css";@import "iSpring/widgets/themes/SystemMenu.css";html,body,div{margin:0;padding:0}</style><script type="text/javascript">dojoConfig = {parseOnLoad:true,baseUrl:'./',modulePaths: { 'iSpring.widgets': 'iSpring/widgets' }};</script><!--<script type="text/javascript" src="http://serverapi.arcgisonline.com/jsapi/arcgis/?v=2.8"></script>--><script type="text/javascript" src="http://localhost/arcgis_js_api/library/2.7/jsapi/"></script><script type="text/javascript" src="three.js"></script><script type="text/javascript" src="Detector.js"></script><script type="text/javascript">dojo.require("esri.map");dojo.require("dijit.dijit");dojo.require("esri.toolbars.draw");dojo.require("esri.tasks.identify");dojo.require("iSpring.widgets.MapToolbar");dojo.require("iSpring.widgets.PipeToolbar");dojo.require("iSpring.widgets.PipeTOC");dojo.require("iSpring.widgets.PipeIdentify");dojo.require("iSpring.widgets.SystemMenu");var map,drawToolbar,extentSymbol,identifyTask,identifyParas;var scene,camera,renderer;var bLeftButtonDown=false;var handleMouseMove;var previousX=-1,previousY=-1;var mapService = "http://localhost/ArcGIS/rest/services/Pipe/MapServer";var StaticRotateRadianY = Math.PI / 180;function init(){map = new esri.Map('mapId');var layer = new esri.layers.ArcGISDynamicMapServiceLayer(mapService);map.addLayer(layer);dojo.connect(map,"onLoad","mapOnLoad");init3D();			animateRefresh();}function animateRefresh(){requestAnimationFrame(animateRefresh);renderer.render(scene,camera);}function mapOnLoad(){drawToolbar = new esri.toolbars.Draw(map);dojo.connect(drawToolbar,"onDrawEnd","drawEnd");dojo.connect(map,'onResize',map,map.resize);extentSymbol = drawToolbar.fillSymbol;drawToolbar.activate(esri.toolbars.Draw.EXTENT);systemMenu.setMap(map);initIdentify();}function createMapToolbar(Map){var bar = new iSpring.widgets.MapToolbar({map:Map});bar.placeAt(dojo.body());bar.startup();}function createPipeToolbar(Scene){var bar = new iSpring.widgets.PipeToolbar({scene:Scene});bar.placeAt(dojo.body());bar.startup();}function createPipeTOC(Scene){var toc = new iSpring.widgets.PipeTOC({scene:Scene});toc.placeAt(dojo.body());toc.startup();}function createPipeIdentify(Attributes){var pipeIdentify = new iSpring.widgets.PipeIdentify({attributes:Attributes});pipeIdentify.placeAt(dojo.body());pipeIdentify.startup();}function initIdentify(){identifyTask = new esri.tasks.IdentifyTask(mapService);identifyParas = new esri.tasks.IdentifyParameters();identifyParas.mapExtent = map.extent;identifyParas.tolerance = 3;identifyParas.returnGeometry = true;identifyParas.layerOption = esri.tasks.IdentifyParameters.LAYER_OPTION_ALL;identifyParas.width = map.width;identifyParas.height = map.height;dojo.connect(identifyTask,"onComplete","identifyCallback");//注意上下文dojo.connect(identifyTask,"onError","identifyErrorback");//注意上下文}function drawEnd(geometry){map.graphics.clear();var graphic = new esri.Graphic(geometry,extentSymbol);map.graphics.add(graphic);doIdentify(geometry);}function doIdentify(geometry){identifyParas.geometry = geometry;identifyTask.execute(identifyParas);}function identifyCallback(results){var center = identifyParas.geometry.getCenter();identifyParas.geometry = null;            if(!scene.initCenter){scene.initCenter = center;}create3D(scene.initCenter,results);}function identifyErrorback(error){identifyParas.geometry = null;console.log(error);}function init3D(){if (!Detector.webgl) {if (Detector.canvas) {renderer = new THREE.CanvasRenderer();}else {alert('Sorry,您的浏览器不支持3D!')}} else {renderer = new THREE.WebGLRenderer({antialias: true});//开启WebGL抗锯齿}scene = new THREE.Scene();var container = dojo.byId("container");var width = dojo.style(container,"width");var height = dojo.style(container,"height");var viewAngle = 45;var aspect = width / height;var near = 0.1;var far = 10000;camera = new THREE.PerspectiveCamera(viewAngle,aspect,near,far);camera.position.set(10,70,80);scene.add(camera);renderer.setSize(width,height);container.appendChild(renderer.domElement);var pointLight = new THREE.PointLight(0xFFFFFF);pointLight.position.x = 10;pointLight.position.y = 50;pointLight.position.z = 130;scene.add(pointLight);camera.target = scene.position;camera.lookAt(scene.position);dojo.connect(window,'resize',function(){var container = dojo.byId("container");var width = dojo.style(container,"width");var height = dojo.style(container,"height");camera.aspect = width / height;camera.updateProjectionMatrix();});dojo.connect(container,'mousedown',onSceneMouseDown);			dojo.connect(container,'mouseup',onSceneMouseUp);dojo.connect(container,'mousewheel',onMouseWheel);dojo.connect(container,'DOMMouseScroll',onMouseWheel);scene.pipeOperationMode="PAN";renderer.render(scene,camera);systemMenu.setScene(scene);}function create3D(center,results){dojo.forEach(results,function(item,index,results){var graphic = item.feature;var uniqueId = graphic.attributes.LayerName+graphic.attributes.OBJECTID;var bExist = checkExist(uniqueId);if(!bExist){if(item.geometryType == "esriGeometryPolyline" && item.layerName.indexOf('管线')>=0){var mesh = createPipeCylinderByGraphic(graphic,center);if(mesh != null){mesh.graphic = item.feature;mesh.uniqueId = uniqueId;scene.add(mesh);}}else if(item.geometryType == "esriGeometryPoint" && item.layerName.indexOf('管点')>=0){createValveModelByGraphic(graphic,center);}}});			}function checkExist(uniqueId){var bExist = dojo.some(scene.children,function(item){if(item instanceof THREE.Mesh){if(item.uniqueId){return item.uniqueId==uniqueId ? true:false;}}});return bExist;}function findMeshByUniqueId(uniqueId){dojo.forEach(scene.children,function(child){if(child instanceof THREE.Mesh){if(child.uniqueId){if(child.uniqueId == uniqueId)return child;}}});return null;}function isZero(number){if(Math.abs(number) < 0.000001){return true;}else{return false;}}function createPipeCylinderByGraphic(graphic,center){Radius = 0.4;var pntNumber = graphic.geometry.paths[0].length;var firstPnt = graphic.geometry.getPoint(0,0);					var x1 = firstPnt.x - center.x;var y1 = parseFloat(graphic.attributes.SCEN_H);var z1 = center.y - firstPnt.y;var lastPnt = graphic.geometry.getPoint(0,pntNumber-1);var x2 = lastPnt.x - center.x;var y2 = parseFloat(graphic.attributes.ECEN_H);var z2 = center.y - lastPnt.y;var color = GetColorByPipeType(graphic.attributes.LayerName);var mesh = createCylinderMesh(x1,y1,z1,x2,y2,z2,Radius,color);mesh.graphic = graphic;mesh.uniqueId = graphic.attributes.LayerName+graphic.attributes.OBJECTID;return mesh;			}function createCylinderMesh(x1,y1,z1,x2,y2,z2,radius,Color){		var x0 = (x1 + x2) / 2;var y0 = (y1 + y2) / 2;var z0 = (z1 + z2) / 2;var p1 = new THREE.Vector3(x1,y1,z1);var length = Math.sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2) + (z1 - z2) * (z1 - z2));var material = new THREE.MeshBasicMaterial( { color: Color } );var geometry = new THREE.CylinderGeometry(radius,radius,length);geometry.applyMatrix( new THREE.Matrix4().setRotationFromEuler( new THREE.Vector3( Math.PI / 2, Math.PI, 0 ) ) );var mesh = new THREE.Mesh(geometry,material);mesh.position.set(x0, y0, z0);		mesh.lookAt(p1);return mesh;}function createValveModelByGraphic(graphic,center){var topH = parseFloat(graphic.attributes.top_h);var bottomH = parseFloat(graphic.attributes.bottom_h);var valveX = graphic.geometry.x - center.x;var valveY = (topH+bottomH)/2 + 1;var valveZ = center.y - graphic.geometry.y;var loader = new THREE.JSONLoader();loader.load('valve.js',function(geometry){var mesh = new THREE.Mesh(geometry,new THREE.MeshFaceMaterial());mesh.position.set(valveX,valveY,valveZ);mesh.rotation.set(1.6,0,0);mesh.scale.set(0.07,0.07,0.07);mesh.graphic = graphic;mesh.uniqueId = graphic.attributes.LayerName+graphic.attributes.OBJECTID;scene.add(mesh);});}function onMouseWheel(evt){var scale = 0.0;if (evt.wheelDelta ){if(evt.wheelDelta > 0){scale = 0.9;}else if(evt.wheelDelta < 0){scale = 1.1;}           }else if(evt.detail){if(evt.detail < 0){scale = 0.9;}else if(evt.detail > 0){scale = 1.1;}}var PreDelta = new THREE.Vector3(camera.position.x - camera.target.x,camera.position.y - camera.target.y,camera.position.z - camera.target.z);var NewDelta = new THREE.Vector3(PreDelta.x * scale, PreDelta.y * scale, PreDelta.z * scale);camera.position.set(camera.target.x + NewDelta.x,camera.target.y + NewDelta.y,camera.target.z + NewDelta.z);camera.lookAt(camera.target);}function onSceneMouseDown(evt){previousX=evt.layerX||evt.offsetX;previousY=evt.layerY||evt.offsetY;bLeftButtonDown = true;handleMouseMove = dojo.connect(dojo.byId("container"),'mousemove',onSceneMouseMove);if(scene.pipeOperationMode=="IDENTIFY"){console.log("scene.pipeOperationMode==IDENTIFY");onIdentifySceneMouseDown(previousX,previousY);}}function onIdentifySceneMouseDown(clickX,clickY){var width=dojo.style("container","width");var height=dojo.style("container","height");var vector = new THREE.Vector3( ( clickX / width ) * 2 - 1, - ( clickY / height ) * 2 + 1, 0.5 );var projector = new THREE.Projector();projector.unprojectVector( vector, camera );var ray = new THREE.Ray( camera.position, vector.subSelf( camera.position ).normalize() );var intersects = ray.intersectObjects( scene.children );if ( intersects.length > 0 ) {//intersects[ 0 ].object.material.color.setHex( Math.random() * 0xffffff );//var particleMaterial = new THREE.ParticleBasicMaterial( {  color: 0x000000});//var particle = new THREE.Particle( particleMaterial );//particle.position = intersects[ 0 ].point;//particle.scale.x = particle.scale.y = 20;//scene.add( particle );createPipeIdentify(intersects[0].object.graphic.attributes);}}function onSceneMouseMove(evt){var currentX=evt.layerX||evt.offsetX;var currentY=evt.layerY||evt.offsetY;if(bLeftButtonDown){if(scene.pipeOperationMode=="PAN"){onPanSceneMouseMove(currentX,currentY);}else if(scene.pipeOperationMode=="ROTATE"){onRotateSceneMouseMove(currentX,currentY);}}previousX = currentX;previousY = currentY;}function onPanSceneMouseMove(currentX,currentY){//左右平移if(currentX != previousX){var bLeft = currentX < previousX ? true:false;var plumbVector	= GetPlumbVector(camera.position, camera.target, bLeft);camera.position.x += plumbVector.x;camera.position.z += plumbVector.z;camera.target.x += plumbVector.x;camera.target.z += plumbVector.z;camera.lookAt(camera.target);}//前后平移if(currentY != previousY){var bForward = currentY < previousY ? true:false;var ForwardVector = GetForwardVector(camera.position,camera.target, bForward);camera.position.x += ForwardVector.x;camera.position.y += ForwardVector.y;camera.position.z += ForwardVector.z;camera.target.x += ForwardVector.x;camera.target.y += ForwardVector.y;camera.target.z += ForwardVector.z;camera.lookAt(camera.target);}}function onRotateSceneMouseMove(currentX,currentY){var RotateRadianY = 0.0;//围绕Y轴旋转的角度var height = parseFloat(dojo.style('container','height'));var deltaY = 0.0;if (currentY >= height/2){if (previousX < currentX){RotateRadianY = StaticRotateRadianY;}if (previousX > currentX){RotateRadianY = -StaticRotateRadianY;}}else if (currentY < previousY){if (previousX > currentX){RotateRadianY = StaticRotateRadianY;}if (previousX < currentX){RotateRadianY = -StaticRotateRadianY;}}if (scene.pipeOperationMode == "ROTATE")//只绕Y轴旋转{camera.position.x = camera.position.x * Math.cos(RotateRadianY) + camera.position.z * Math.sin(RotateRadianY);camera.position.z = -camera.position.x * Math.sin(RotateRadianY) + camera.position.z * Math.cos(RotateRadianY);camera.target.x = camera.target.x * Math.cos(RotateRadianY) + camera.target.z * Math.sin(RotateRadianY);camera.target.z = -camera.target.x * Math.sin(RotateRadianY) + camera.target.z * Math.cos(RotateRadianY);camera.lookAt(camera.target);}}function onSceneMouseUp(evt){bLeftButtonDown = false;currentX=-1;currentY=-1;dojo.disconnect(handleMouseMove);}function GetPlumbVector(FromPoint,ToPoint,bLeft){var Vector3D = new THREE.Vector3(ToPoint.x-FromPoint.x,ToPoint.y-FromPoint.y,ToPoint.z-FromPoint.z);var PlumbVector2D;var FromPoint2D = new THREE.Vector3(FromPoint.x,0,FromPoint.z);var ToPoint2D = new THREE.Vector3(ToPoint.x,0,ToPoint.z);var Vector2D = new THREE.Vector3(ToPoint2D.x-FromPoint2D.x,0,ToPoint2D.z-FromPoint2D.z);Vector2D.normalize();PlumbVector2D = new THREE.Vector3(-Vector2D.z,0,Vector2D.x);PlumbVector2D.normalize();if (bLeft) {var K = Vector2D.z / Vector2D.x;if (PlumbVector2D.z > K * PlumbVector2D.x) {PlumbVector2D.x *= -1;PlumbVector2D.z *= -1;}}else {var K = Vector2D.z / Vector2D.x;if (PlumbVector2D.z < K * PlumbVector2D.x){PlumbVector2D.x *= -1;PlumbVector2D.z *= -1;}}            return PlumbVector2D;}function GetForwardVector(FromPoint, ToPoint, bForward){var Vector3D = new THREE.Vector3(FromPoint.x-ToPoint.x,FromPoint.y-ToPoint.y,FromPoint.z-ToPoint.z);var FromPoint2D = new THREE.Vector3(FromPoint.x,0,FromPoint.z);var ToPoint2D =  new THREE.Vector3(ToPoint.x,0,ToPoint.z);var Vector2D = new THREE.Vector3(ToPoint2D.x - FromPoint2D.x,ToPoint2D.y - FromPoint2D.y,ToPoint2D.z - FromPoint2D.z);Vector2D.normalize();if (bForward){Vector2D.x *= -1;Vector2D.z *= -1;}           return Vector2D;}function GetColorByPipeType(PipeType){var PipeColor;var type = PipeType;if (PipeType.indexOf("管线")>=0){type = PipeType.replace(/管线/, "");} else if (PipeType.indexOf("管点")>=0){type = PipeType.replace(/管点/, "");}switch (type){case "工业":{PipeColor = 0x000000;break;}case "污水":case "雨水":{PipeColor = 0xD85B00;break;}case "煤气":{PipeColor = 0x19E2E8;break;}case "电信":{PipeColor = 0x00ff00;break;}case "电力":case "路灯":{PipeColor = 0xff0000;break;}case "给水":{PipeColor = 0x0000ff;break;}}return PipeColor;}dojo.addOnLoad(init);</script>
</head>
<body class="claro"><div data-dojo-type="iSpring.widgets.SystemMenu" jsId="systemMenu"></div><div style="position:absolute;top:120px;"><div id="mapId" style="width:600px;height:500px;float:left;border:solid 2px #000"></div><div id="container" style="width:600px;height:500px;float:left;margin-left:20px;border:solid 2px #000"></div></div>
</body>
</html>



注:转载请注明出处

这篇关于WebGL+ArcGIS JS API实现Web城市地下管线三维场景浏览的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/725724

相关文章

Spring Boot整合Redis注解实现增删改查功能(Redis注解使用)

《SpringBoot整合Redis注解实现增删改查功能(Redis注解使用)》文章介绍了如何使用SpringBoot整合Redis注解实现增删改查功能,包括配置、实体类、Repository、Se... 目录配置Redis连接定义实体类创建Repository接口增删改查操作示例插入数据查询数据删除数据更

Java Lettuce 客户端入门到生产的实现步骤

《JavaLettuce客户端入门到生产的实现步骤》本文主要介绍了JavaLettuce客户端入门到生产的实现步骤,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要... 目录1 安装依赖MavenGradle2 最小化连接示例3 核心特性速览4 生产环境配置建议5 常见问题

linux ssh如何实现增加访问端口

《linuxssh如何实现增加访问端口》Linux中SSH默认使用22端口,为了增强安全性或满足特定需求,可以通过修改SSH配置来增加或更改SSH访问端口,具体步骤包括修改SSH配置文件、增加或修改... 目录1. 修改 SSH 配置文件2. 增加或修改端口3. 保存并退出编辑器4. 更新防火墙规则使用uf

Java 的ArrayList集合底层实现与最佳实践

《Java的ArrayList集合底层实现与最佳实践》本文主要介绍了Java的ArrayList集合类的核心概念、底层实现、关键成员变量、初始化机制、容量演变、扩容机制、性能分析、核心方法源码解析、... 目录1. 核心概念与底层实现1.1 ArrayList 的本质1.1.1 底层数据结构JDK 1.7

C++中unordered_set哈希集合的实现

《C++中unordered_set哈希集合的实现》std::unordered_set是C++标准库中的无序关联容器,基于哈希表实现,具有元素唯一性和无序性特点,本文就来详细的介绍一下unorder... 目录一、概述二、头文件与命名空间三、常用方法与示例1. 构造与析构2. 迭代器与遍历3. 容量相关4

C++中悬垂引用(Dangling Reference) 的实现

《C++中悬垂引用(DanglingReference)的实现》C++中的悬垂引用指引用绑定的对象被销毁后引用仍存在的情况,会导致访问无效内存,下面就来详细的介绍一下产生的原因以及如何避免,感兴趣... 目录悬垂引用的产生原因1. 引用绑定到局部变量,变量超出作用域后销毁2. 引用绑定到动态分配的对象,对象

SpringBoot基于注解实现数据库字段回填的完整方案

《SpringBoot基于注解实现数据库字段回填的完整方案》这篇文章主要为大家详细介绍了SpringBoot如何基于注解实现数据库字段回填的相关方法,文中的示例代码讲解详细,感兴趣的小伙伴可以了解... 目录数据库表pom.XMLRelationFieldRelationFieldMapping基础的一些代

Java HashMap的底层实现原理深度解析

《JavaHashMap的底层实现原理深度解析》HashMap基于数组+链表+红黑树结构,通过哈希算法和扩容机制优化性能,负载因子与树化阈值平衡效率,是Java开发必备的高效数据结构,本文给大家介绍... 目录一、概述:HashMap的宏观结构二、核心数据结构解析1. 数组(桶数组)2. 链表节点(Node

Java AOP面向切面编程的概念和实现方式

《JavaAOP面向切面编程的概念和实现方式》AOP是面向切面编程,通过动态代理将横切关注点(如日志、事务)与核心业务逻辑分离,提升代码复用性和可维护性,本文给大家介绍JavaAOP面向切面编程的概... 目录一、AOP 是什么?二、AOP 的核心概念与实现方式核心概念实现方式三、Spring AOP 的关

Python实现字典转字符串的五种方法

《Python实现字典转字符串的五种方法》本文介绍了在Python中如何将字典数据结构转换为字符串格式的多种方法,首先可以通过内置的str()函数进行简单转换;其次利用ison.dumps()函数能够... 目录1、使用json模块的dumps方法:2、使用str方法:3、使用循环和字符串拼接:4、使用字符