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应用中的实现过程

《分布式锁在SpringBoot应用中的实现过程》文章介绍在SpringBoot中通过自定义Lock注解、LockAspect切面和RedisLockUtils工具类实现分布式锁,确保多实例并发操作... 目录Lock注解LockASPect切面RedisLockUtils工具类总结在现代微服务架构中,分布

Java使用Thumbnailator库实现图片处理与压缩功能

《Java使用Thumbnailator库实现图片处理与压缩功能》Thumbnailator是高性能Java图像处理库,支持缩放、旋转、水印添加、裁剪及格式转换,提供易用API和性能优化,适合Web应... 目录1. 图片处理库Thumbnailator介绍2. 基本和指定大小图片缩放功能2.1 图片缩放的

Python使用Tenacity一行代码实现自动重试详解

《Python使用Tenacity一行代码实现自动重试详解》tenacity是一个专为Python设计的通用重试库,它的核心理念就是用简单、清晰的方式,为任何可能失败的操作添加重试能力,下面我们就来看... 目录一切始于一个简单的 API 调用Tenacity 入门:一行代码实现优雅重试精细控制:让重试按我

MySQL常用字符串函数示例和场景介绍

《MySQL常用字符串函数示例和场景介绍》MySQL提供了丰富的字符串函数帮助我们高效地对字符串进行处理、转换和分析,本文我将全面且深入地介绍MySQL常用的字符串函数,并结合具体示例和场景,帮你熟练... 目录一、字符串函数概述1.1 字符串函数的作用1.2 字符串函数分类二、字符串长度与统计函数2.1

Redis客户端连接机制的实现方案

《Redis客户端连接机制的实现方案》本文主要介绍了Redis客户端连接机制的实现方案,包括事件驱动模型、非阻塞I/O处理、连接池应用及配置优化,具有一定的参考价值,感兴趣的可以了解一下... 目录1. Redis连接模型概述2. 连接建立过程详解2.1 连php接初始化流程2.2 关键配置参数3. 最大连

Python实现网格交易策略的过程

《Python实现网格交易策略的过程》本文讲解Python网格交易策略,利用ccxt获取加密货币数据及backtrader回测,通过设定网格节点,低买高卖获利,适合震荡行情,下面跟我一起看看我们的第一... 网格交易是一种经典的量化交易策略,其核心思想是在价格上下预设多个“网格”,当价格触发特定网格时执行买

Java Stream流之GroupBy的用法及应用场景

《JavaStream流之GroupBy的用法及应用场景》本教程将详细介绍如何在Java中使用Stream流的groupby方法,包括基本用法和一些常见的实际应用场景,感兴趣的朋友一起看看吧... 目录Java Stream流之GroupBy的用法1. 前言2. 基础概念什么是 GroupBy?Stream

python设置环境变量路径实现过程

《python设置环境变量路径实现过程》本文介绍设置Python路径的多种方法:临时设置(Windows用`set`,Linux/macOS用`export`)、永久设置(系统属性或shell配置文件... 目录设置python路径的方法临时设置环境变量(适用于当前会话)永久设置环境变量(Windows系统

SpringBoot监控API请求耗时的6中解决解决方案

《SpringBoot监控API请求耗时的6中解决解决方案》本文介绍SpringBoot中记录API请求耗时的6种方案,包括手动埋点、AOP切面、拦截器、Filter、事件监听、Micrometer+... 目录1. 简介2.实战案例2.1 手动记录2.2 自定义AOP记录2.3 拦截器技术2.4 使用Fi

Python对接支付宝支付之使用AliPay实现的详细操作指南

《Python对接支付宝支付之使用AliPay实现的详细操作指南》支付宝没有提供PythonSDK,但是强大的github就有提供python-alipay-sdk,封装里很多复杂操作,使用这个我们就... 目录一、引言二、准备工作2.1 支付宝开放平台入驻与应用创建2.2 密钥生成与配置2.3 安装ali