12三维GIS开发基础_第1页
12三维GIS开发基础_第2页
12三维GIS开发基础_第3页
12三维GIS开发基础_第4页
12三维GIS开发基础_第5页
已阅读5页,还剩43页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

12三维GIS开发基础12.1三维GIS简介二维GIS始于二十世纪八十年代的机助制图,今天已深入到社会的各行各业中,如土地管理、电力、电信、城市管网、水利、消防、交通以及城市规划等。但二维GIS存在着自身难以克服的缺限,本质上是基于抽象符号的系统,不能给人以自然界的本原感受。随着应用的深入,第三维的高程信息显得越来越重要。一些二维GIS和图象处理系统现已能处理高程信息,但它们并未将高程变量作为独立的变量來处理,只将其作为附属的属性变量对待,能够表达出表面起伏的地形,但地形下而的信息却不具有,因此它们在国际国内也被俗称为2.5维的系统。2.5维这一概念并不严密,我们认为,2.5维GIS本质上仍然是二维GIS系统。二维GIS只能处理平面X、Y轴向上的信息,不能处理铅垂方向Z轴上的信息。它在表达上通常是将Z值投影到二维平面上进行处理,因此对于同一(x,y)位置的多个Z值不能表达。2.5维的表达将Z值投影到一个模型上,显示时X、Y、Z三个轴均被显示,其模仿人类从某点观察的视觉,使2.5维对象看起來象真正的三维对象一样。但是2.5维技术有两个明显的缺点:D它表达的对象内部是空的,不备应冇的信息;②虽然它能表现邻近的多个表面,但对于表面交义的情况,则难以进行交义表达利管理。只有将这类现象置于真正的三维空间中考虑,才能灵活高效地处理各种三维问题,如三维内部属性和拓扑关系,三维空间索引和管理等。这是三维空间表达与二维GIS、2.5维表达的本质区别之一。三维空间表达考虑多个Z值的出现,将多个(X,Y,Z)观测点结构化为实体域,这种处理是对人类居住空间的较为接近的近视。12.1.1三维GIS的定义从不同的角度出发,GIS有三种定义:①基于工具箱的定义,认为GIS是一个从现实世界采集、存贮、转换、显示空间数据的工具集合;②数据库定义,认为GIS是一个数据库系统,在数据库里的大多数数据能被索引和操作,以回答冬种各样的问题:③基于组织机构的定义,认为GIS是一个功能集合,能够存贮、检索、操作和显示地理数据,是一个集数据库、专家和持续经济支持的机构团体和组织结构,提供解决环境问题的各种决策支持。基于工具箱的定义强调对地理数据的齐种操作,基于数据库的定义强调用来处理空间数据的数据组织的差异,而基于组织的定义强调机构和人在处理空间信息上的作用,而不是他们需要的工具的作用。TURNER认为“GeogiapliicalIiifonnationSystem"主要用來区分纯粹的二维GIS与三维GIS,为强调在三维任务如地质或地貌应用上的扩展,人们创造了术语wGeoscientitleDiiormationSystem”(GSIS)。后来这个词被修改为一个缩写形式-Geo-IiifonnationSystem”(GIS)。为区分三维GIS与现今世界上比较成熟的流行的各种二维商业GIS,这里倾向于BREUNIG的观点,用GIS指R^Geo-IiifonnationSystem”,认为三维GIS是布满整个三维空间的GIS,与传统的基于平面的二维GIS或2.5维GIS明显不同,尤其体现在空间位置与拓扑关系的描述及空间分析的伸展方向上。12.1.2三维GIS的开发方式现在的三维GIS开发主要有三种途径:一是通过底层开发实现,如VC卄和OpenGL的开发方式;二是在三维可视化软件上通过插件的形式加载数据査询和显示的功能模块,如Vega实时驱动软件:三是在现有的GIS平台上二次开发实现,很多的GIS软件商都提供了相应的二次开发包,代表为ArcGIS的AicObject组件:这三种开发方式各有利弊:>底层开发的方式软件效率较高,冋时具有跨平台的特性。但是缺点在于代码屋较大,开发周期较长,而且只能针对开放的数据格式;>以三维建模和三维可视化为主的三维软件,在三维模型制作方而已经有了很好的成就,具有很强的模拟度和逼真性。但它们不能或者只能提供很少的GIS功能,如属性显示,不能达到三维GIS的要求。如Vega实时软件可以导入OpenFlight格式的三维模型,在虚拟的三维场景中实现漫游、导航、环境效果切换、对环境模型进行査询等功能,但它不具备GIS的空间分析功能。>在传统的GIS软件中,在二维半面上的GIS空间分析功能已II臻完善,能提供从基本的GIS数据创建、更新、查询、制图和分析到高级功能模块扩展,适应了不同层次用户的需要。但是,传统的GIS软件都以GIS分析为主,三维可视化方而的功能较为欠缺。以ArcGIS中的三维分析模块为例,它主要是提供了基fDEM或考TIN的地形表面的显示、査询、分析,虽然提出了Multipatch的三维数据模型,但是在三维模型的构建和编辑上还存在不足,模型真实感不强。因此,在成熟的GIS软件如ArcGIS软件的基础上进行二次开发,并结合二维建模软件构造的三维模型,将会是目前比较合适的开发方式。本章内容即着重介绍在ArcGIS的ArcObject基础上进行的三维GIS的二次开发。12.2基于ArcObjects的三维开发2010年7月,Esri发布了AicGIS软件的最新版本ArcGIS10,为构建完善的GIS系统,提供了一套完整的软件产品。其中ArcGIS3D分析扩展模块是ArcGIS桌面产品的三维可视化和分析扩展模块。这个可选的ArcGIS3D分析扩展模块包禽两个专用的三维可视化应用程序:AicScene和ArcGlobe。ArcScene是基J:内存的应用主要完成三维数据创建及其小场景可视化。AicGlobe提供交互式全球海量地理数据三维可视化可以实现全球,地方和街道数据一级级无缝转换。ArcGISlO实现了三维建模、数据编辑、要素存储、性能优化以及分析的巨大飞跃,具有经济的数据获取与处理:海量数据的存储和管理:快速创建三维可视化场景;海屋数据的高效可视化;强大的GIS分析功能。为了满足不断发展的三维应用需求,并拓展ArcGIS产品市场,ArcGIS支持基fArcObjects的三维GIS二次开发。开发者可以在AicGIS提供的相关组件上开发出满足特殊需求的三维GIS软件。在AicObjects中用于三维开发的主要类库包括了Display类库、3DAnalyst类库、GlobeCore类库、SpatialAnalyst类库、GeoAnalyst类库、Controls类库。3DAnalyst类库包含了操作三维场景的对象,Scene对象是3DAnalyst类库主要对象之一,它与Map对象类似,是一个数据的容器。一个场景由一个或多个图层组成,这些图层规定了场景中包含的数据以及这些数据如何显示。GlobeCore类库包含操作globe数据的对象,Globe对象是GlobeCore类库中组要对象之一,与Map对象和Scene对象一样也是数据容器。一个globe由一个或多个图层组成,这些图层规定了globe中包含的数据以及这些数据如何显示。SpatialAnalyst类库和GeoAiialyst类库主要是用于各种数据模型的三维分析。Controlsl类库包含了用于快速开发应用程序的各种控件,比如SceneConhol控件、GlobeControl控件、ToolBarControl和TocControl等等。为高效而便捷的开发基于AicObjcets的三维应用程序,AicGIS为开发者提供两个主要的功能控件:SceneControl控件和GlobeControl控件。SceneControl控件封装了SceneViewer对象,这和ArcScene应用程序是类似的,使用SceneControl控件开发的应用程庁适合生成远景三维场景,让我们能进行导航并与三维要素和栅格数据互动。基T-OpenGL,三维场景支持复杂的三维线性符号和纹理映射以及表面创建和TIN显示。在SceneControl控件中,三维场景中的所有数据都加载到了内存中,这样我们能相对快速的进行导航、漫游和缩放功能,因此SceneControl更适合开发用于数据量少的小型研究区域的应用程序。在SceneContiol控件中显示的欠量耍素只作为矢量來渲染,栅格数据也显示在你所设置的固定数目的行列上。GlobeControl控件封装了GlobeViewer对象,这个控件定义的属性和方法都与ArcGlobe应用程序类似,因此使用GlobeControl控件开发的应用程序更适合用J:海量数据集和无缝的栅格和要素数据可视化。GlobeControl基于三维地球视图,所有的数据投射到了一个globeCubeprojection上,能在多样的详细程度(LOD)上进行显示,每个层次都组织到瓦片上,为了最大限度发挥性能,将数据进行缓存,这样能组织和拷贝源数据到瓦片LODs上了。在GlobeControl控件屮显示的欠量要素将被栅格化并根据它们关联的LOD进行显示,这对快速导航和显示很有帮助。12.3ArcGIS三维数据模型MultipatchMultipatch对象多而体(MultiPatches)描述具有多个带纹理的表而。多面体存储了顶点法线,顶点的标识,顶点测最值以及构件属性。你能通过导入多种文件格式的数据来创建多面体,如3DShictioMax.3dsfiles,OpenFliglitfitfiles,Sketchup.skpfiles,VRML.wrlfiles等等:你也能用以下方法通过编程來创建多面体。>创建不带纹理、法线和构件属性的MultiPatch能用类似创建名边形的方式来定义:创建构件,创建多面体,然后用最新版本的IGeometryCollection接口来添加构件即可。>创建一个带纹理.法线和构件属性的MultiPatch需要使用GeneralMiiltiPatchCreator对象(需要3DAnalyst许可)。通过使用IGeneralMultiPatcliIiifo接口你能获取一个己知多而体的材质和法线信息。Multipatch对象实现的主要接口包括:IMultiPatch接IGeometiy接口、IGeomebyCollection接口、IPointCollection接口、IRelationalOperator3D接口、IToplogicalOpeiator3D接口、IProxiniityOperator3D接口、IVolume接口等等。下面代码实现计算一个多面体的体积。注意,如果构造的多面体的构件节点的顺序发生错误,比如正面不朝外,那么将得到一个负的体积值:PublicstaticvoidCalculateVolmileO{IGeometrygeometry=GetMultiPatchGeometryO;IVohimevolume=geometryasIVolume;doublecalculatedVolume=volume.Volume;//calculatedVolume=225.04812.3.2Multipatch构件MultiPatches包含『多个构件:三角带(TYiangleStrips),三角扇(TriangleFans),三角形(TYiangles)和环(rings)o每个构件都有儿个额外的属性:Type(TOangleShip,TOangleFan,TOangle,OuterRing,etc)Priority—用于控制重叠构件的绘制顺序Anindexintothearrayofmaterials•三角带(niangleStrips)三角带是表而块的一种,由点集來定义并决定了组成的三角表面。如有六个点的二角带,二角形表面被这些点定义:(0,1,2),(2,1,3),(2,3,4),(4,3,5)。前面或正面是通过顺时针定向前三个顶点建立的。•三角扇(TYiangleFans)三角扇是表面块的一种,由点集来定义并决定了组成的三角表面。和三角带不同,三角扇是由3D三角形的集合组成,它的第一个点定义尖端顶点或原点,并包含在所有的三角型表面中。如有八个点的三角扇,三角形表面被这些点定义:(0,1,2),(0,2,3),(0,3,4),(0,4,5)。它的前面是通过顺时针定向前三个顶点建立的。环用于构造多边形和多面体。2D校验规则对于MultiPatch并不受其约束,所以你能用一个包含垂直定向环的多面体去表现垂直的墙。一个给定的环不一定就是二维的。换句话说,它的顶点不是一定都要位于一个二维平面上,一个非平面的环内部通过确定它如何被镶嵌來定义。当前使用OpenGL功能类库中的tessellator(镶嵌器)来完成。多面体中的环组能定义一个带洞的表面。环类型(FirstRing,OuterRing,IimerRing等等)在添加到MultiPatch后立即被指定到环。>外环:MultiPatch表面的外部或外环(类似一个多边形的外环)>内环:MultiPatch表面内部或洞(类似一个多边形的内环)>第一个环:没有指明包含关系的环组的第一个环>环:没有指明包含关系的环组的另一个环典型的外环序列组合代表了表面的外部边界,接着的很多内部环代表了洞。当在环集A中单独的环表达一个带有洞的未知多边形块,就必须从第一个环开始押序,然后紧跟着很多的环。一个不以第一环开始的环序列,将被当作没有洞的外环序列。为了防上用没有洞或内部的环来定义多面体,一般使用基本环,尽管外环也能很好的表达。12.3.3三维空间关系运算MultiPatches支持IRelationalOperator3D2接口,这个接口包含了Disjoint3D方法和IsNearJD方法。Digoint3D(IGeometrypother)方法返回一个布尔值,如果为False那么可以判断两个三维儿何体相交,如果为nue那么它们相离。IsNeare3D(IGeometrypOtliei;doubledistance)方法返M的也是一个布尔值,用來显示这个三维几何体是否在其它三维几何体在给定的距离内。下面代码实现判断两个具有Z值的儿何是否相交:IGeometiypolylineGeometry=GetPolylineGeometryO;IGeomehypolygonGeomehy=GetPolygonGeometryO;IRelationalOperator3DrelationalOperator3D=polylineGeometiyasIRelatioiialOperator3D;boolintersect=!(rdationalOperator3D.Disjoint3D(polygonGeometry));//intersect=tmeMultiPatch也支持IProximityOperator3D接口,这个接口定义了QueryNearestPoint3D方法、RehiniDistance3D方法和RetiimNearestPoint3D方法。QueryNeaiestPoint3D(IPointplnp、esriSegmentExtensionextension,IPointpNearest)方法将返回的点拷贝到运算几何里。RetmiiDistance3D(IGeometrypOtlier)方法返回两个儿何的最短距离,如果两个几何相交那么最短距离为0。RetiirnNearestPoint3D(IPointphip.esriSeginentExtensionextension)方法在输入儿何上创建并返回离输入点最近的一个点,如果运算儿何是一个包络线,点在其内部,那么返回的最近点将在包络线的外部。下面代码实现在包络线中查询一个与输入点最近的点:publicstaticvoidQueiyNearestPoint3D0〃获取一个三维点IGeomehypointGeometry=GetPointGeometry0;〃获取用于运算的包络线IGeometryenvelopeGeonietry=GetEnvelopeGeometry0IProximityOperator3DAproximityOperator3D=envelopeGeometryasIProximityOperator3D;〃进行查询,并返回最近的三维点IPointnearestPoint3D=newPointClassO;pioximityOpeiator3D・QueryNeaiestPoint3D(pointGeometryasIPoint,esriSegiiientExtension.esiiNoExtension.nearestPoint3D);//nearestPoint3D=A(5.393,0583,6043)

12.3.4MultiPatches中的表面法线(surfacenormals)每个MultiPatch顶点都有一个稳定的表面法线与它相关联。没有明确稳定的表面法线,只能形成平直的面阴影(独立的三角形)。没有这个法线,光照方程式不能内插穿越而以实现平滑阴影。有了表面法线,我们就能用很少的儿何数据來平滑地渲染弯曲的表面。12.3.5材质(Materials)和纹理(Textures)MultiPatches能存储材质,材质只是向多面体的外观添加细节而并不增加儿何的复杂性。每个构件能引用一个材质,当然一个材质能被多个构件引用。材质包括了颜色、整体透明度和可选的纹理,它有一个透明色或一个alpha通道。纹理坐标设置了纹理的位置。纹理是一副栅格影像,覆盖在多面体的表面。JPEG格式的斥缩栅格文件能被作为纹理使用。在一个多面体创建对象的环境中,材质的索引(index)和类型(type)是同样的意思,通过引用一个多面体中的材质索引可以符号化一个构件。添加纹理时,带有浮点值纹理坐标对与顶点关联。与多而体相关联的纹理顶点集能识别渲染系统,这个渲染系统定义了如何去覆盖纹理。纹理坐标正常的范围是[0,1]。值大于1会引起纹理沿坐标轴平铺。下图显示纹理数据(左边),纹理坐标:(u,v)对,几何坐标(x,y,z)元组和多面体渲染模式(右边)之间的关系。•纹理坐标系统的原点在左上角。用纹理來创建多面体需耍使用GeometiyMaterialCOM对象(3DAiialyst类库),一个GeometryMaterials数组即GeometiyMaterialList和GeneralMiiltiPatchCreator(3DAnalyst类库)。GeneralMultiPatchCreator对象将材质和儿何信息聚合到一个多面体的shapefilebuffers中,然后用它创建成一个多面体。这个过程要求所有的纹理数据在内存中扩展开來。MiiltiPatches身支持包倉床缩纹理数据的sliapefilebuffeiso不同的多面体不能共享纹理数据。下面代码实现用三角带來创建带纹理的MultiPatclio假定三角带类似一个垂直的多边形,它的大小为300(width)x100(lieiglit):publicIMultiPatchCreateMultipatchO{〃准备几何材质列表IGeomehyMaterialtextlire=newGeometiyMaterialClassO;texture.Textureimage=nC:\\Temp\\MyIinage.bmprl;〃向材质列表中添加纹理IGeometiyMaterialListmaterialList=newGeometiyMaterialListClassO;materialList.AddMaterial(textlire);〃创建多面体IGeneralMultiPatchCreatormiiltiPatchCreator=newGeneralMultiPatchCreatorClassO;multiPatchCreator.Iiiit(4,1,false,false,false,4,materialList);〃设置构件为三角带multiPatchCreator.SetPatchl\pe(0,esriPatchiype.esriPatchiypelYiangleStrip);miiltiPatchCreator.SetMaterialIndex(0,0);multiPatchCreator.SetPatcliPointIiidex(0,0);multiPatchCreator.SetPatchTextiirePointIndex(0,0);〃设置实际坐标点WKSPointZupperLeft=newWKSPointZO;WKSPointZlowerLeft=newWKSPointZO;WKSPointZupperRiglit=newWKSPointZO;WKSPointZlowerRiglit=newWKSPointZO;upperLeft.X=0;upperLeft.Y=0;uppeiLeft.Z=0;upperRiglit.X=300;upperRiglit.Y=0;upperRiglit.Z=0;lowerLeft.X=0;lowerLeft.Y=0;lowerLeft.Z=-100;lowerRiglit.X=300;lowerRiglit.Y=0;lowerRiglit.Z=-100;multiPatchCreator.SetWKSPointZ(O,refupperRiglit);multiPatchCreator.SetWKSPointZ(l,reflowerRiglit);multiPatchCreator.SetWKSPointZ(2,refupperLeft);multiPatchCreato匚SetWKSPointZ(3,reflowerLeft);〃设置纹理点,纹理坐标WKSPointtextiireUpperLeft=newWKSPointO;WKSPointtextiueLowerLeft=newWKSPointO;WKSPointtextmeUpperRigilt=newWKSPointO;WKSPointtextiueLowerRiglit=newWKSPointO;textiireUpperLeft.X=0;textiireUpperLeft.Y=0;textiireUpperRiglit.X=1;textiireUpperRight.Y=0;textiueLoweiLeft.X=0;textureLowerLeft.Y=1;textiireLowerRiglit.X=1;textiireLowerRiglit.Y=1;miiltiPatchCreator.SetTextiireWKSPoint(0,reftextiireUpperRight);multiPatchCreator.SetTextureWKSPoint(l,reftextiireLowerRigilt);multiPatchCreator.SetTextuieWKSPoint(2,reftextureUpperLeft);multiPatchCreator.SetTextureWKSPoint(3,reftextiireLowerLeft);EMultiPatchmultiPatch=multiPatchCreator.CreateMultiPatchOasIMultiPatch;rehirnmultiPatch;}12.4使用SceneControl控件开发SceneControl控件SceneControl功能类似与ArcScene中的三维视图,它提供了三维显示和研究小型空间数拯图层的方法。SceneControl封装了SceneViewer对象,实现了ISceneViewer接口。这和ArcScene内部的ISceneViewer对象是一样的oIScene\newer对象包含了一个Camera,Camera包含了一个Observer和Target位置oSceneControl提供了快捷的方式去使用它内部封装对象的属性和方法。比如,SceneControl包禽TCameraScene、SceneGiaph和SceneViewer对象屈性。这些对象都定义在3DAiialyst类库中。SceneControl还提供了额外的属性、方法和事件用于:>管理控件的外观、显示属性和地图属性:>装载Scene文档(sxd)到控件中;>添加并管理控件中的数据层;>管理Scene、SceneGraph和Camera属性;>设置当前工具,在可视化环境中,可以通过控件的“属性”页设置控件的相关属性,也可以通过编设置。如下图是一个简单SceneControl的应用程序界面:

叱Forml・6*◎令G住欧B 岀0P*-0Scenelayers-<FightData巳UFOV♦匚FightPath-<SurfaceData>7Domain■vDomain□dtm.tin-“ImageDataW□MeshVPhoto.*pTopo.sdRGBRed:Band.Green:Band.But:Band.SceneControl控件具有内置的导航功能,它在设计时可以通过SceneContiol属性页或导航属性编程改变。这个功能能让最终用户做到以下体验:>用鼠标左键向后向前导航,向左向右显示;>用鼠标右键在显示中放大和缩小在SceneControl控件中,我们还能通过SceneControl屈性页(在开发环境中支持属性页功能)设置三维场景文档SceneDocumentoSceneControl在文档创建以后便可以直接加载到控件中,当然也可以编程实现加载一个三维场景文档。SceneConhol对象提供了CheckSxFile(stringfilepath)方法來检验这个文档是否有效,并使用LoadSxFile(stringfilepalit)方法來加载这个文档。以下代码实现了在SceneControl中加载文档:stringfilePath=@,,C:\Temp\MySceneDocumefit.sxdn;if(axSceneControll.CheckSxFile(filePath))axSceneControll.LoadSxFile(filePath);SceneControl实现的主要接口包括:ISceneControlDefaultISceneConhol和该控件的事件接口ISceneControlEvents。12.4.1.USceneControlDefault接口ISceneControlDefault接口是三维场景控件缺省接口,多数开发环境白动使用这个接口定义的属性、方法。由于SceneControl是一个自动化控件,当它被放到一个容器••如窗体上后,它会〔I动产生一个被称为axSceneContioll的对象,这个对象可以直接使用缺省接口定义的属性和方法。这个接口也代表了控件最新版本的接口。当需要使用这个接口的时候,可以使用下而的代码:ISceneControlDefaultsceneControl= axSceneControl1.Object asISceneControlDefault;.ISceneControl接口这个接口是任何一个与SceneControl相关任务的起点,如设置控件的外观,向控件中添加Scene文档,管理Scene>SceneGraph和Camera对象,以及设置当前工具,在控件中绘制图形等。在VisualC#.NET中,当査询接口【SceneControl时,Object属性或容器定义代码必须使用,因为.NET包含的控件是由包装对象控制。下面代码实现了ISceneConhol接口:ISceneControlsceneControl=axSceneControil.ObjectasISceneControl;.ISceneControlEvents事件接口ISceneControlEvents是一个事件接口,它定义了SceneControl能够处理的全部事件,如OnDoubleClick、OiikeyDown、O11MouseDown、O11MouseMove、O11MouseUp、OnSceneReplaced等,这些事件在建立独立的三维应用程序时经常用到,与MapControl事件接口相似,不同的是由于三维视图操作更为复杂,因此与键盘操作互动的OnKeyDown事件也较为常用。鼠标与控件的交互•三维场景漫游在SceneControl中通过移动Camera来实现三维场景的移动。下面的代码使用\Camera的Pan(IPointpStartPoint,IPointpEndPoint)方法在SceneControl的OnMouseDown和OiiMouseMove事件中实现三维场景的漫游:PublicoverridevoidOnMouseDowii(intButton,intShift,intX,intY){〃获取鼠标起始点坐标nilMouseX=X;m」MouseY=Y;}PublicoverridevoidOiiMouseMove(intButton,intShift,intX,intY){if(X-mlMouseX=0&&Y-MouseY=0)retiun;〃用起始点坐标创建点IPointpStartPoint;pStartPoint=newPointClassO;pStartPoint.PxitCoords((double)m」MouseX,(double)mlMouseY);//用起始点坐标创建点EPointpEndPoint;pEndPoint=newPointClassO;pEndPoint.PutCoords((double)X,(double)Y);//获取鼠标移动坐标mlMouseX=X;mlMouseY=Y;〃获取SceneViewer和CameraISceneViewerpSceneViewer;pSceneViewer=axSceneControll.SceneViewer;[CamerapCamera=PSceneViewer.Camera;〃移动camera以实现漫游pCamera.Pan(pStartPoint,pEndPoint);〃重绘sceneviewer刷新pSceneViewer.Rediaw(false);//重绘sceneviewer}•在SceneControl中绘制图形在三维场景中,由于三维几何与二维几何的差异,一些二维图形的绘图方法不能在三维场景中直接使用。并且如果儿何没有高度属性,在三维场景中将无法对它进行渲染,所以要在SceneControl中绘制图形并对它渲染就需要获取绘制点的三维坐标,或苕直接调用图形设备接口对绘制的几何进行渲染。下面展示了两种绘制和渲染几何的方法,一种通过得到三维场景中的三维点,使用创建图形反馈如INewLineFeedback来得到三维儿何,然后根据这个三维图形,在图形图层中添加三维要素,绘后三维场景对它进行渲染:另一种直接在窗口中绘制一个二维几何,然后调用图形设备接口GDI+对它进行渲染下面代码实现了第一种方法,在三维场景中绘制一条三维多义线:publicovenidevoidOnMouseDown(intButton,intSliift,intX,intY){ISceneGraphpSceneGiaph=msceneHookHelper.SceneGraph;ISceneViewerpSceneViwer=pSceneGiaph.ActiveViewer;objectppOwner,ppObject;pSceneGraph.Locate(pSceneViwer,X,Y,esriScenePickMocle.esriScenePickAll,tme,outStartpt,outppOwner,outppObject);if(Startpt=null)return;if(pNewLineFeedback==null){pNewLineFeedback=newNewLineFeedbackClassO;pNewLineFeedback.Display=pSceneGiaphasIScreeiiDisplay;pNewLineFeedback.Start(Startpt);}else{pNewLineFeedback.AddPoint(Startpt);}}publicoveiridevoidOnMouseMove(intButton,intShift,intX,intY)if(Staitpt=null)return;IPointpt=newPointClassO;pt.PntCoords((double)XXdouble)Y);pt.Z=Startpt.Z;if(pNewLineFeedback!=null)pNewLineFeedback.MoveTo(pt);}publicoverridevoidOiiDblClickO{IGeometiypgeo;pgeo=pNewLineFeedback.Stop();if(pgeo=null)retuni;pNewLineFeedback=null;//对反馈得到的图形进行渲染ILineElementlineEle=newLineElementClassO;ISimpleLineSyinbolpSimpleLineSyinbol=newSimpleLineSynibolClass();pSimpleLiiieSyinbol.Color.RGB=255;lineEle.Symbol=pSimpleLineSyinbol;IElementpElement=lineEleasIElemeiit;pElement.Geometiy=pgeo;//将元素添加到图形图层中,然后刷新IGraphicsContainer3DpGrapliicsContainer3D=msceneHookHelper.Scene.BasicGraphicsLayerasIGraphicsContainer3D;pGiaphicsContainer3D.AddElement(pElemeiit);liisceneHookHelper.SceneGiaph.RefieshViewersO;nisceneHookHelper.SceneGiaph.ActiveViewer.Rediaw(false);}-下面代码实现了第二种绘制方法,在窗口中绘制一个二维的矩形。这种绘制图形的方法效率更高,在不需要Z值时,一般选用这种方法。publicoveiridevoidO11MouseDown(iutButton,intShift,intX,intY){mJMouseX=X;m」MouseY=Y; 〃获取鼠标的初始化坐标SetCaphire(pSceneViewei.hWnd);〃捕捉SceneViewer的当前窗口}publicoveiridevoidOnMouseMove(intButton,intShiftintX,intY){IEnvelopepEnvelope;〃进行判断,设置包络线的最大和最小XY值if((double)m_lMouseX<=(double)X){pEnvelope.XMin=(double)mlMouseX;pEnvelope.XMax=(double)X;}else{pEnvelope.XMin=(double)X;pEnvelope.XMax=(double)mlMouseX;if((double)mlMouseY<=(double)Y){pEnvelope.YMin=(double)m」MouseY;pEnvelope.YMax=(double)Y;}else{pEnvelope.YMin=(double)Y;pEnvelope.YMax=(double)mlMouseY;}〃根据得到的包络线绘制矩形 一using(GiaphicsmyGiaphics=Giapliics.FromHdc((IiitPtr)pSceneViewer.hDC))using(Bnishbmsh=newSolidBmsh(Color.Bansparent))//hoilowbrush{〃调用图形设备接口(GDI+)的空心刷去填充矩形myGraphics.FillRectangle(bnish,(int)pEnvelope.XMin,(int)pEnvelope.YMin,(int)pEnvelope.Width,(int)pEnvelope.Heiglit);}using(Penpen=newSystem.Drawing.Pen(Color.Black,2))//使用画笔绘制{myGrapliics.DrawRectangle(peii,(int)pEnvelope.XMin,(int)pEnvelope.YMin,(int)pEnvelope.Width,(int)pEnvelope.Heigilt);}•在SceneControl中放大三维场景中进行放大是通过改变Camera的属性來实现的,有三种方法,一种通过改变视场角度大小來实现,一种通过Camera的Zoom。方法,传入一个缩放比例实现,还一种是通过Camera的ZoomToRec0方法,传入一个矩形实现。下而代码实现在SceneControl中点击后改变Camera的目标点,并进行•缩放:publicoveiridevoidOnMouseDown(intButton,intShift,intX,intY){//ICamerapCamera=pSceneViewer.Camera;〃获取cameraISceneGraphpSceneGraph=pScene\^ewer.SceneGraph;//获取SceneGraphIPointpPoint; objectpOwner,pObject;pSceneGraph.Locate(pSceneGiaph.ActiveViewei\X,Y,esriScenePickMode.esriScenePickAll,true,outpPoint,outpOwner,outpObject);〃重新设置照相机的目标点并放大pCamera.Target=pPoint;pCameia.Zoom(0.75);}下面代码实现了在三维场景中拉框放大。因为三维图形比二维图形绘制更为复杂,而传入的矩形图形只要二维图形即可,所以这里通过调入图形设备接口GDI+來渲染这个矩形。publicoveiridevoidO11MouseDown(intButton,intShift,intX、intY)〃获取鼠标的初始化坐标mbliiUse=tnie;mIMouseX=X;mIMouseY=Y;ISceneViewerpSceneViewei=GetSceneViewerO;〃获取sceneviewerSetCaptiire(pSceneViewer.hWnd);//捕捉SeeneViewer当前窗口}publicoverridevoidOnMouseMove(intButton,intShiftintX,intY){if(!m_bhiUse)retiini;IEnvelopepEnvelope;CreateEnvelopeQC,XoutpEnvelope);DrawRectangle(pEnvelope);//绘制矩形}publicoverridevoidOiiMouseUp(intButton,intSliift,intX、intY){if(GetCaptiire(pSceneViewer.hWnd)!=0)ReleaseCaptiire(m_pSceneHookHelper.ActiveViewer.hWnd);ICamerapCamera=pSceneViewer.Cameia;〃获取cameraISceneGiaphpSceneGraph=pSceneViewei\SceneGiaph;〃获取SceneGraphIEnvelopepEnvelope;CreateEnvelope(X.Y,outpEnvelope);IPointpPoint;objectpOwner,pObject;if(pEnvelope.Widtli==0||pEnvelope.Heiglit=0){〃将屏幕上鼠标获取的坐标转换为三维点pSceneGraph.Locate(pSceneGiaph.ActiveViewei\X.Y,esiiScenePickMode.esriScenePickAll,true,outpPoint,outpOwnei;outpObject);pCamera.Target=pPoint;pCamera.Zoom(0.75);//设置camera目标点并放大}else{〃设置照相机的投影类型,这里设置为三维场景投影if(pCamera.Piojectioniype=esri3DPiojectioniype.esiiPerspectiveProjection){pCamera.ZoomToRect(pEnvelope);//Zoomcameratotheenvelope}else{pSceneGraplLLocate(pSceneGiaph.ActiveViewei;(int)(pEiivelope.XMiii+(pEnvelope.Width/2))Xint)(pEnvelope.YMin+(pEnvelope.Heiglit/2)),esriScenePickMode.esriScenePickAll,true,outpPoint,outpOwner,outpObject);pCamera.Target=pPoint;〃设置camera目标点〃获取Sceneviewer窗口的大小Rectanglerect;rect=newRectangle。;if(GetWindowRect(pSceneViewer.hWnd5refrect)=0)return;doubledx,dy;dx=pEnvelope.Width;dy=pEnvelope.Heigilt;//Determinezoomfactorif(dx>0&&dy>0)dx=dx/Math.Abs(rect.Rigilt・rect.Left);dy=dy/Math.Abs(rect.Top・rect.Bottom);if(dx>dy)pCameia.Zoom(dx);elsepCamera.Zoom(dy);}elsepCamera.Zoom(0.75);}pSceneViewei.Rediaw(true);}•定位观察点在三维场景中,如果想要在三维场景中的定位一个新的观察点进行观察,就要重新设置Camera的Oberser属性,下面代码在OnMouseUp事件中实现了定位观察位置:PublicoverridevoidOnMouseUp(intButton,intSliift,intX,intY){IPointpNewObs;objectpOwner,pObject;〃将屏幕上鼠标获取的坐标转换为三维点pSceneGiaph.Locate(pSceneViewer,X,Y,esiiScenePickMode.esiiScenePickAll,tme,outpNewObs,outpOwner,outpObject);ICamerapCamera=pSceneViewer.Camera;〃获取camera原观察点IPointpOldObs=(IPoint)pCamera.Observer;〃获取重绘的间隔周期和每秒钟帧的半均数doubledlastFrameDuration,dMeanFrameRate;pSceneGiaph.GetDrawingllmeliifoCoutdlastFrameDuiatioil,outdMeaiiFrameRate);〃控制三维场景在改变时的速度if(dlastFrameDuration<0.01)dlastFrameDuration=0.01;intiSteps; iSteps=(int)(2/dlastFrameDuration);if(iSteps<1)iSteps=1; if(iSteps>60)iSteps=60;doubledxObs,dyObs,dzObs;dxObs=(pNewObs.X-pOldObs.X)/iSteps;dyObs=(pNewObs.Y-pOldObs.Y)/iSteps;dzObs=(pNewObs.Z•pOklObs.Z)/iSteps;〃通过每一个步骤依次移动观察点和目标并刷新三维场景视图for(inti=0;i<=iSteps;i-H-){pNewObs.X=pOldObs.X+(i♦dxObs);pNewObs.Y=pOldObs.Y+(i*dyObs);pNewObs.Z=pOldObs.Z+(i*dzObs);pCamera.Observer=pNewObs;pSceneGraph.ActiveViewer.Reclraw(tnie);•定位目标中心定位目标中心可以操纵目标位置,当你在三维场景中点击新的冃标位置时,

观察者的位置仍然保持不变,这和你站着不动然后转动你的头去看东西是类似的。

设置目标中心,实际上就是设置了照相机的位置,然后旋转镜头去观察三维场景。PublicoverridevoidOnMouseUp(intButton,intShiftintX、intY){IPointpNewTgt;o切ectpOwner,pObject;〃将屏幕上鼠标获取的坐标转换为三维点pSceneGraph.Locate(pSceneGraph.ActiveViewer,X、Y,esiiScenePickMode.esriScenePickAll,tiue,outpNewTgt,outpOwner,outpObject);ICamerapCamera=pSceneGraph.ActiveViewer.Camera;〃设置为固定比例尺的投影类型if(pCamera.PrqjectionI\pe=esri3DPrqjectioniype.esriOrthoPrqjection){pCamera.Tsrget=pNewTgt;//SetthecainerafsnewtargetpSceneGraph.ActiveViewer.Redraw(tnie);//Redrawthesceneviewer}else{IPointpOldTgt,pOldObs;〃获取camera原目标和观察点

pOldTgt=pCamera.Target;pOldObs=pCamera.Observer;〃设置camera新的目标以及获得新的观察点pCamera.Target=pNewTgt;pCameia.PolarUpdate(l,0,0、tiue);IPointpNewObs=(IPoint)pCamera.Obseiver;〃获取重绘周期和每秒钟帧的平均数最doubleellastFrameDuration,dMeanFrameRate;pSceneGiaph.GetDrawinglimeliifofoutcflastFrameDuration^outdMeanFiameRate);if(dlastFrameDiuation<0.01)dlastFrameDuration=0.01;intiSteps;iSteps=(int)(2/dlastFrameDuration);if(iSteps<1)iSteps=1;if(iSteps>60)iSteps=60;doubledxObs,dyObs,dzObs;doubledxTgt,dyTgt,dzTgt;dxObs=(pNewObs.X・pOldObs.X)/iSteps;dyObs=(pNewObs.Y-pOldObs.Y)/iSteps;dzObs=(pNewObs.Z•pOklObs.Z)/iSteps;dxTgt=(pNewTgt.X・pOldTgt.X)/iSteps;dyTgt=(pNewTgt.Y-pOldTgt.Y)/iSteps;dzTgt=(pNewTgt.Z・pOlcTTgt.Z)/iSteps;〃依次通过每一个步骤去移动观察点和目标并刷新三维场景视图for(inti=0;i<iSteps;i卄){pNewObs.X=pOldObs.X+(i♦dxObs);pNewObs.Y=pOldObs.Y+(i*dyObs);pNewObs.Z=pOldObs.Z+(i*dzObs);pNewTgt.X=pOldTgt.X+(i*dxTgt);pNewTgt.Y=pOldTgt.Y+(i♦dyTgt);pNewTgt.Z=pOldTgt.Z+(i*dzTgt);pCamera.Obseiver=pNewObs;pCamera.Target=pNewTgt;pSceneGiaph.ActiveViewer.Rediaw(tnie);}•在三维场景中飞行飞行让你能通过飞越观察你要研究的三维场景,它让你能在任何方向移动,并用不同的速度向前或向后。飞行主要是控制目标位置,在鼠标点击的方向上向前或向后移动,这样观察者就相对的移动了。当然,操控飞行需要一定的练习。PublicoverridevoidOnMouseUp(intButton,intSliift,intX,intY){m」MouseX=X;mlMouseY=Y;if(m_iSpeed==0)StartFliglitO;}~"PublicoverridevoidOnMouseMove(intButton,intShift,intX,intY){if(!mbliiUse)return;m」MouseX=X;m」MouseY=Y;}~~PublicvoidStartFliglitO{//获取scenegiaph的范围LEnvelopepEnvelope;pEnvelope=pSceneGraph.Extent;〃查询这个范围的坐标doubledXmin,dXmax,dYmin,dYmax;pEnvelope.QueryCoords(outdXiiiin,outdYmin,outdXinax,outdYmax);〃设置三维场景移动速度if((dXinax-dXinin)>(dYmax•dYniin))mdMotion=(dXinax-dXmin)/100;elsemdMotion=(dYmax-dYmin)/100;〃获取cmnera的目标和观察点ICameiapCamera=pSceneViewer.Camera;m_pPointObs=pCamera.Obseiver;m_pPointTgt=pCamera.Target;〃计算观察点和目标的距离doubledx,dy.dz;dx=m^pPointTgt.X・ni_pPointObs.X;dy=m_pPointTgt.Y•m_pPointObs.Y;dz=m_pPointTgt.Z•m_pPointObs.Z;〃确定高程、方位角以及观察点和目标间的距离mdElevation=Math.Atan(dz/Math.Sqrt(dx*dx+dy*dy));indAzimut=Matli.Atan(dy/dx);indDistance=Math.Sqit((dx5,1dx)+(dy*dy)+(dz*dz));FliglitQ;//飞行PublicvoidFliglitQdoubledlastFrameDuration,dMeaiiFrameRate;〃获取绘制时间间隔pSceneGraph.GetDrawingHmeliifoCout cilastFrameDuiation, outdMeanFrameRate);if(dlastFrameDuration<0.01)dlastFrameDuiation=0.01;if(dlastFrameDuration>1) dlastFrameDuration=1;〃调用WindowsAPI接口获取窗体坐标Rectanglerect=newRectangle();if(GetClientRect(pSceneViewer.hWnd.refrect)=0)rehim;doubledXMouseNoimal,dYMouseNonnal;〃获取法线向量dXMouseNormal=2*((double)m」MouseX/(double)rect.Riglit)-1;dYMouseNormal=2*((double)m_lMouseY/(double)rect.Bottom)・1;〃设置法线旋转的高程和方位角 "nidElevation=mdElevation•(dlastFrameDuration♦dYMouseNonnal♦Math.Abs(dYMouseNormal));mdAzimut=mdAzimut-(dlastFrameDuration*dXMouseNonnal*Math.Abs(dXMouseNonnal));if(in_dElevation>0.45*3.141592)mdElevation=0.45*3.141592;if(m_dElevation<-0.45*3.141592)mdElevation=-0.45*3.141592;if(m_dAzimut<0)mdAzimut=mdAziinut+(2♦3.141592);if(m_dAzimut>2*3.141592)mdAzimut=mdAzimut•(2*3.141592);doubledx,dy5dz;dx=Math.Cos(m_dElevatioil)♦Math.Cos(mdAzimut);dy=Math.Cos(mdElevation)*Math.Sin(mdAzimut);dz=Math.Sin(m_dElevation);〃改变目标点方肓m_pPointTgt.X=m_pPointObs.X+(mdDistance*dx);m_pPointTgt.Y=m_pPointObs.Y+(mdDistance*dy);ni_pPointTgt.Z=m_pPointObs.Z+(mdDistance♦dz);〃在视线方向移动照相机 一m_pPointObs.X=m_pPointObs.X+(dlastFrameDuratioii*(2Am_iSpeed)*m_dMotion*dx);m_pPointObs.Y=in_pPointObs.Y+(<ilastFranieDuration*(2Am」Speed)*m_dMotion*dy);iii_pPoiiitTgt.X=m_pPoiiitTgt.X+(dlastFiaiiieDuratioii*(2Am_iSpeed)*m_dMotion*clx);m_pPointTgt.Y=m_pPoiiitTgt.Y+(dlastFraineDuratioii*(2Am」Speed)*mdMotion*dy);m_pPointObs.Z=mjpPointObs.Z+(dlastFrameDuration*(2Am」Speed)木m_dMotion♦dz);m_pPointTgt.Z=in_pPointTgt.Z+(dlastFiameDuration*(2Am_iSpeed)*mdMotion*dz);pCamera.Observer=m_pPointObs;pCamera.Target=m_pPointTgt//设置观察点和目标点间的视线角度pCamera.RollAngle=10*dXMouseNormal*Math.Abs(dXMouseNonnal);pSceneViewer.Rediaw(tnie);//RedrawthesceneviewerobjectobjCancel;if(bCancel=tme)EndFliglitQ;〃如果ESC键取消E行PublicvoidEndFliglitOmbhiUse=false;IPointpPointTgt;pPointTgt=newPointClassO;objectpOwner,pObject;Rectanglerect=newRectangle();//WindowsAPIcalltogetwindowsclientcoordinatesif(GetClientRect(m_pSceneHookHelper.ActiveViewer.hWnd5reflect)!=0){〃转换屏幕坐标为三维点pSceneGraph.Locate(pSceneViewer,rect.Riglit/2,rect.Bottom/2,esiiScenePickMode.esriScenePickAlUtme,outpPointTgt,outpOwnei;outpObject);}ICamerapCamera=GetCameraO;if(pPointTgt!=null){〃重置观察点和目标pCamera.Target=pPointTgt;pCamera.Observer=m_pPointObs;}〃设置观察点和目标的之间的camera角度pCamera.RollAngle=0;pCamera.PropertiesCliangedO;m」Speed=0;}~•在SceneControl中进行导航三维导航是通过使用一个Camera对象实现的。所有的三维视图都有一个单一的Camera,^个Camera包含了观察者的位置(三维视图中Camera摆放的位置)和目标位置(Camera朝向的三维点)。三维导航是通过操纵观察者和目标位置來实现的。下面代码实现了在SceneControl中进行简单的导航:publicoverridevoidO11MouseDown(intButton,intShift,intX,intY){SetCapture(pSceneViewer.hWnd);//mlMouseX=X;mlMouseY=Y;}一"publicovenidevoidO11MouseMove(intButton,intShiftintX5intY){if(X・m」MouseX=0&&Y-mJMouseY==0)rehim;longdx,dy;dx=X-mlMouseX;dy=Y-m」MouseY;if(Button==2)//Ifriglitmouseclicked{〃设置缩放鼠标,并进行缩放SetCursor(m_pCxirsorZoom.Handle.ToIiit320);if(dyv0)pSceneViewer.Caniera.Zoom(l.l);elseif(dy>0)pSceneViewer.Camera.Zooni(0.9);}if(Button==1)//单击鼠标左键//Ifsceneviewergesturingisdisabledmovethecameraobseiverif(m_bGestme==false)pSceneViewer.Camera.PolarUpdate(l,dx,dy5true);else{if(m_bSpinning=tme)//如果正在进行旋转{"pSceneViewer.Camera.PolarUpdate(l.dx,dy.tnie);}else{//WindowsAPIcalltogetwindowsclientcoordinatesRectanglerect;rect=newRectangle();GetClientRect(pSceneViewer.liWild,refrect);〃计算旋转的步长if(dx<0)mdSpinStqj=(180.0/rect.Riglit・rect.Left)*(dx-pSceneViewer.GestiireSensitivity);elsemdSpinStep=(180.0/rect.Right・rect.Lefl)*(dx+pS

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论