ArcGIS Engine栅格数据使用总结3D开发部分.docx
《ArcGIS Engine栅格数据使用总结3D开发部分.docx》由会员分享,可在线阅读,更多相关《ArcGIS Engine栅格数据使用总结3D开发部分.docx(24页珍藏版)》请在冰豆网上搜索。
ArcGISEngine栅格数据使用总结3D开发部分
ArcOjects3D开发方法简介
作者:
佚名文章来源:
GIS论坛点击数:
861更新时间:
2009-8-6
一、ArcOjects3D开发方法简介
众所周知,在ArcGIS3D分析扩展模块中提供了丰富的三维可视化和分析功能:
你可以通过不同的视角查看表面数据,对表面数据进行查询,以及对表面数据进行坡度、坡向、视域分析等操作,进行三维动画模拟等等。
其中所涉及的3D对象都是ArcObjects的一部分,针对3D的开发,实际上是ArcObjects的开发,所以具体的开发方法有:
基于ArcScene中内嵌的VBA开发;
通过VB、VC++等兼容COM的开发语言进行开发新的3D组件和功能。
二、基本的3D对象模型
在3D开发中,我们可以用ArcMap对应ArcScene,其中MxDocument对象对应SxDocument对象,Map对象对应着Scene对象,而相对于Display显示对象,在ArcScene中有SceneGraph对象。
在对象模型图的顶部是Application对象,从它我们可以执行和应用相关的任务,比如打开文档或者访问和应用相关的其它对象。
在VBA中,我们可以直接获得Application对象:
DimpAppasIApplicaiton
SetpApp=Application
如果你在VBDLL中实现命令和工具,那么在具体实例化这个类时你可以获得和Application对象挂接的钩子(hook):
ImplementsICommand
Privatem_pAppasesriCore.Iapplication
PrivateSubICommand_OnCreate(ByValHookAsObject)
Setm_pApp=Hook
…
EndSub
有了Application对象,你就可以访问它所包含的其它所有对象了。
比如可以获得SxDocument对象,而它包含一个Scene对象,Scene对应的SceneGraph对象包含了一个或多个SceneViewer对象,每个SceneViewer对象中有一个Camera对象,它代表了观察点的特性。
在Scene对象中可以访问Layer对象和GraphicsLayer3D对象,这两个对象都包含着一个3DProperties对象,用来控制图层中有关三维方面的特性。
具体有关对象的特性可以参考联机帮助中对象上提供的接口所属的方法和属性。
三、3D几何模型和定制对象介绍
3D模型可以包括两种:
矢量模型和表面模型,表面模型包括TIN和Raster,有关表面模型的创建、数据结构访问和分析在本文中不做介绍。
在此只介绍三维矢量模型的生成和访问等。
3D矢量模型包括所有含有Z值的几何对象:
点、线、面,以及MultiPatch(多片)。
其中多片又可以分为:
三角条带(TriangleStrip)、三角扇(TriangleFan)和环(Ring)。
在ArcScene中可以通过二维的点、线、面数据来构建三维模型,通过ArcScene中提供的拉伸功能可以将点要素构建成垂直的线,线要素构建成墙,而多边形要素构建成块,拉伸的值的大小可以是一定常数,也可以是通过要素属性字段中的值计算得出,或者通过数据自身记录的Z值。
在ArcObjects中可以通过在Geometry几何对象构建过程中,任意点除了X、Y坐标值,指定坐标Z值来构建三维点、线和面对象;对于多片,则通过构建相应的Multipatch对象,并指定每一个顶点的X、Y和Z值。
下面的代码描述了如何由多片来构建一个房子对象,它的房顶由三角扇构建,没有窗户的墙由三角条带构建,带窗户的墙由环构建。
DimpMultiPatchAsesriCore.IMultiPatch
SetpMultiPatch=NewMultiPatch
DimpGeoColAsesriCore.IGeometryCollection
SetpGeoCol=pMultiPatch
DimpPointsAsesriCore.IPointCollection
DimpPointAsIPoint
‘创建屋顶
SetpPoints=NewesriCore.TriangleFan
SetpPoint=NewPoint
pPoint.PutCoords5,4
pPoint.Z=10
pPoints.AddPointpPoint
SetpPoint=NewPoint
pPoint.PutCoords0,0
pPoint.Z=5
pPoints.AddPointpPoint
‘屋顶的其它顶点:
...(10,0,5);(10,8,5);(0,8,5);(0,0,5)
‘将扇加到MultiPatch
pGeoCol.AddGeometrypPoints
‘为没有窗户的墙创建条带
SetpPoints=NewesriCore.TriangleStrip
‘添加条带顶点:
...(10,0,5);(10,0,0);(10,8,5)
...(10,8,0);(0,8,5);(0,8,0)
...(0,0,5);(0,0,0)
‘将条带添加到MultiPatch
pGeoCol.AddGeometrypPoints
‘为前面的墙创建外环
SetpPoints=NewesriCore.Ring
‘添加外环顶点:
...(10,0,5);(10,0,0);(10,8,5)
...(10,8,0);(0,8,5);(0,8,0)
...(0,0,5);(0,0,0)
‘将外环添加到MultiPatch
pGeoCol.AddGeometrypPoints
pMultiPatch.PutRingTypepPoints,esriMultiPatchOuterRing
‘为前面的墙创建内环
SetpPoints=NewesriCore.Ring
‘添加内环顶点:
...(1,0,2);(3,0,2);(3,0,4);(1,0,4);(1,0,2)
pGeoCol.AddGeometrypPoints
pMultiPatch.PutRingTypepPoints,esriMultiPatchInnerRing
SetpPoints=NewesriCore.Ring
‘添加内环顶点:
...(7,0,2);(9,0,2);(9,0,4);(7,0,4);(7,0,2)
pGeoCol.AddGeometrypPoints
pMultiPatch.PutRingTypepPoints,esriMultiPatchInnerRing
‘设置Z和M坐标awareness
DimpZAwareAsesriCore.IZAware
SetpZAware=pMultiPatch
pZAware.ZAware=True
DimpMAwareAsesriCore.IMAware
SetpMAware=pMultiPatch
pMAware.MAware=False
通常,多片是用来描述阴影平面集合,并且可以在其上粘贴影像,也就是贴图。
为了粘贴影像,需要在Multipatch的每一个定点上记录纹理坐标。
纹理坐标记录为s和t,值为0到1,表示了从x到y方向影像的起点到终点。
在ArcObjects中将s和t编码存储在定点的M值中。
另外,如果要用Multipatch表达复杂连续的几何对象,比如球体,为了平滑阴影平面之间的转折,需要在Multipatch的每一个顶点上记录它的法向量,同样法向量也是编码后存储在定点的M值中。
如下面的代码:
DimpNormalAsesriCore.IVector3D
DimpVertexAsesriCore.Ipoint
DimmAsDouble
m=0
...
DimpEncoderAsIEncode3DProperties
SetpEncoder=NewGeometryEnvironment
pEncoder.PackNormalvNormal,m
pEncoder.PackTexture2Ds,t,m
pVertex.M=m
四、3D渲染和动画功能定制简介
在ArcObjects中的3D对象内部调用了OpenGL图形库,所以有关3D渲染方面的开发是基于OpenGL图形库方面的知识。
在ArcObjects中的定制渲染工作原理如下图,在SceneGraph:
:
Redraw绘制开始和结束时分别会激活SceneGraph或其它外部事件处理器的OnBeforeDraw和OnAfterDraw事件,我们可以在这两个事件添加自己的渲染效果,比如在OnBeforeDraw事件中添加雾化效果等,而可以在OnAfterDraw事件中添加三维文字标注等。
动画使得场景有动态的感觉,我们可以通过对视角、图层的可见性、以及场景的属性进行定制来达到动画的效果。
下图是在动画定制中将会涉及到的部分类和接口。
从此对象模型图我们可以看出在Scene对象中可以访问动画轨迹对象(AnimationTrack),而动画轨迹是由关键帧组成的,关键帧分成场景的、图层的和书签(照相机)的。
同时动画类型也分为场景、图层和照相机三种。
这些类都是CoClass,可以通过创建的方式实例化,从而来控制动画的定制。
另外,在ArcObjects中提供了控制动画演播和录制的相应方法,具体参考帮助文档中AnimationTrack类和ScreenViewer类。
五、三维控件
在ArcObjects中包含一个ActiveXviewer控件,它为ActiveX控件容器如MicrosoftWord、MicrosoftPowerPoint等提供了显示三维文档、图层及图形数据的功能。
下面的代码显示了如何在VB中使用此控件:
‘引用ArcScene文档
SceneViewerCtrl1.DocName=“c:
\temp\myscene.sxd”
或者
‘添加一个图层
SceneViewerCtrl.SceneGraph.Scene.AddLayerpLayer
或者
‘添加一个图形元素
DimpMapAsIBasicMap
SetpMap=SceneViewerCtrl1.SceneGraph.Scene
DimpGConAsIGraphicsContainer3D
SetpGCon=pMap.BasicGraphicsLayer
SetpGCon=pGLayer
pGCon.AddElementpElement
Viewer.RedrawFalse
六、总结
本文旨在对ArcObjects?
中有关3D的部分做一个简单的介绍,
本篇文章来源于GIS空间站转载请以链接形式注明出处网址:
ArcGISServer动态生成缓存
作者:
liyuanxi…文章来源:
ESRIChina点击数:
480更新时间:
2009-9-2
使用ArcGISServer做地图发布,为了提升浏览性能,通常会使用现时比较流行的地图缓存技术(通俗的说法为“瓦片技术”)。
如目前的MapABC和GoogleMap正是使用该技术。
所谓的地图缓存技术,就是按照一定的数学规则,把地图切成一定规格的图片保存到计算机硬盘里,当用户通过客户端浏览器访问地图服务时,服务器直接返回当前地图坐标区域所对应的“瓦片”,从而达到降低服务器负担,提升地图浏览速度的效果。
地图缓存技术一般针对相对稳定的数据,因为地图切为瓦片以后,以图片的形式存在,对于数据的变化(这里指的是数据的几何形状变化)不能及时的反应,这就是地图缓存技术不足之处。
要想地图的变化得到及时的反映,那就必须重建地图缓存。
而重建地图缓存要视地图的区域范围和缓存的比例尺而定,时间为几分钟到几十个小时不等。
因此,缓存的管理是一件相对麻烦的事情。
对实时性要求比较高的系统来说,一般不建议使用地图缓存技术。
但地图缓存带来的性能的体验非常良好,因此可以在此基础上进行一些改动,使其适应地图的更新操作十分必要。
某些WebGIS系统由于涉及数据的编辑,数据更新频率较大,不适用缓存的方式发布,数据的实时性非常好,但地图的浏览和刷新性能非常差(刷新性能与数据的大小和图层的渲染复杂度有关),大量占用服务器资源,多用户连接的时候导致服务器不稳定等。
经过反复的试验,针对上述的需求,懒羊羊提出了以下的一种方案,以解决数据频繁变动和地图性能低下的问题。
方案的基本思路:
使用地图缓存技术对地图进行切片;编辑数据的时候获取空间数据对应的瓦片(一张或者几张);计算这部分瓦片的地图范围,并在后台重新生成这个范围的地图图片;把新生成的图片替换这些旧有的瓦片。
具体的做法如下:
1.
创建一个非池化的服务,并生成地图缓存。
这部分懒羊羊不作介绍。
2.
获取编辑的图形所对应的瓦片。
一般来说,如果是点图形,对应的是一张瓦片,线、面图形一个图形有可能落在多张瓦片上面。
可以使用ESRI.ArcGIS.ADF.ArcGISServer.TileCacheInfo来获取瓦片的相关信息,但具体落在那一张瓦片,那就必须了解地图切片原理。
1)ArcGISServer切片原理与命名规则设定一个原点作为地图切片的起始点(默认是(400,-400),这是个经纬度坐标,设这个值可以把其他地区的数据连接进来,使不同服务的数据可以得到有效的拼接,有兴趣的同事可以研究一下),以一定的规格(长宽为2的n次方的像素)把地图切割成若干的小图片,并以科学命名的方式存贮到计算机磁盘。
命名的规则是各比例尺的图片放在名为LXX的文件夹里面,第一个比例尺的文件夹名为L00,第二个比例尺的问L01,如此类推。
比例尺文件夹(一下统称L文件夹)目录下还会有R开头的文件夹,R表示的ROW,当前比例尺的瓦片每一行对应一个文件夹。
R文件夹的命名方式是瓦片的行序列(用rIndex表示),把rIndex转为8位16进制,不足的在左边补0,用代码公式表示FolderName="R"+rIndex.ToString("x").PadLeft(8,'0')。
R文件夹里面保存的就是瓦片,瓦片的命名方式跟R文件夹的命名方式相似,以字母C开头,后面是瓦片在该行的列序号(用cIndex)表示,后面依然是一个8位16进制FileName="C"+rIndex.ToString("x").PadLeft(8,'0')+"."+format.ToString()
2)计算图形对应的瓦片(以点为例)
下面以加入一个点要素为例,说明如何去获取这个点对应的瓦片
首先获取地图服务的相关信息,其中NotPooledServiceUrl是字符串,对应当前服务的URL,地图服务已切片
//获取服务的相关信息
ESRI.ArcGIS.ADF.ArcGISServer.MapServerProxymapserver_dcom=newESRI.ArcGIS.ADF.ArcGISServer.MapServerProxy(NotPooledServiceUrl);
ESRI.ArcGIS.ADF.ArcGISServer.MapServerInfomapi=mapserver_dcom.GetServerInfo(mapserver_dcom.GetDefaultMapName());
StringdefaultMapName=mapserver_dcom.GetDefaultMapName();
ESRI.ArcGIS.ADF.ArcGISServer.MapDescriptionpMapDescription=mapi.DefaultMapDescription;
ESRI.ArcGIS.ADF.ArcGISServer.EnvelopeNmape=mapi.FullExtentasESRI.ArcGIS.ADF.ArcGISServer.EnvelopeN;
ESRI.ArcGIS.ADF.ArcGISServer.EnvelopeNmapiExtent=(ESRI.ArcGIS.ADF.ArcGISServer.EnvelopeN)mapi.Extent;
然后获取这个这个地图服务的瓦片信息
//获取瓦片的图片格式
stringcacheTileFormat=mapserver_dcom.GetTileImageInfo(defaultMapName).CacheTileFormat;
stringimageType=cacheTileFormat.StartsWith("PNG")?
".png":
".jpg";
//获取瓦片的相关信息
ESRI.ArcGIS.ADF.ArcGISServer.TileCacheInfotCacheInfo=mapserver_dcom.GetTileCacheInfo(defaultMapName);
//瓦片的原点(ags默认值为(-400,400),在切片的时候可以设定这个值)
PointNoriginPT=tCacheInfo.TileOriginasPointN;
接着定义其他相关的信息
doubleenvCenterX=pPoint.X;
//pPoint为新加入的点要素的图形
doubleenvCenterY=pPoint.Y;
ESRI.ArcGIS.ADF.ArcGISServer.LODInfo[]lodInfos=tCacheInfo.LODInfos;
inttColCenter,tRowCenter;
//pPoint所在的瓦片对应的列、行
doubletileWidth,tileHeight;
//对应瓦片的长度和宽度(计算后为地图单位)
doubletileXMin,tileYMin;
//pPoint所在的瓦片的左下角坐标
doubletileXMax,tileYMax;
//pPoint所在的瓦片的右上角坐标
通过计算找到这个点对应的各级缓存的瓦片的路径(根据上述所说的切片原理和命名规则算出来)
foreach(ESRI.ArcGIS.ADF.ArcGISServer.LODInfoliinlodInfos)
{
tileWidth=tCacheInfo.TileCols*li.Resolution;
tColCenter=(int)Math.Floor((envCenterX-(double)originPT.X)/tileWidth);
tileXMin=(double)originPT.X+(tColCenter*tileWidth);
tileHeight=tCacheInfo.TileRows*li.Resolution;
tRowCenter=(int)Math.Floor(((double)originPT.Y-envCenterY)/tileHeight);
tileYMin=((double)originPT.Y-(tRowCenter*tileHeight))-tileHeight;
tileXMax=tileXMin+tileWidth;
tileYMax=tileYMin+tileHeight;
stringtUrl=cacheDir+"\\Layers\\_alllayers"+"\\L"+li.LevelID.ToString().PadLeft(2,'0')
+"\\R"+tRowCenter.ToString("x").PadLeft(8,'0')
+"\\C"+tColCenter.ToString("x").PadLeft(8,'0')
+imageType;
UpdateTile(tileXMin,tileYMin,tileXMax,tileYMax,tUrl,tCacheInfo,poolService)
//分别更新各个比例尺下的瓦片,其中poolService是一个MapServerProxy
}
计算瓦片行列值的公式完全可以通过切片原理推出来
Column=Floor((PointofinterestX–TileoriginX)/Groundwidthofatile)
Row=Floor((TileoriginY–PointofinterestY)/Groundheightofatile)
最后是通过生成新的图片去取代原来的瓦片,以达到局部更新的效果(也就是上面的UpdateTile函数的工作)。
实现的思路是通过传入单张瓦片对应的四个角的坐标去定义一个Envelope,输出这个Envelope区域的图片,并替换掉对应url的瓦片。
ESRI.ArcGIS.ADF.ArcGISServer.MapServerInfoagsSoapMapServerInfo=mapserver_dcom.GetServerInfo(mapserver_dcom.GetDefaultMapName());
ESRI.ArcGIS.ADF.ArcGISServer.MapDescriptionagsSoapMapDescription=agsSoapMapServerInfo.DefaultMapDescription;
SRI.ArcGIS.ADF.ArcGISServer.EnvelopeNpEnv=newESRI.ArcGIS.ADF.ArcGISServer.EnvelopeN();
pEnv.XMin=xMin;
pEnv.YMin=yMin;
pEnv.XMax=xMax;
pEnv.YMax=yMax;
agsSoapMapDescription.MapArea.Extent=pEnv;
//设定了出图区域
//设定出图图片的格式,dpi,长宽等
ESRI.ArcGIS.ADF.ArcGISServer.ImageDescriptionagsImageDes=newESRI.ArcGIS.ADF.ArcGISServer.ImageDescription();
ESRI.ArcGIS.ADF.ArcGISServer.ImageTypeagsImageType=newESRI.ArcGIS.ADF.ArcGISServer.ImageType();
agsImageType.ImageFormat=ESRI.ArcGIS.ADF.ArcGISServer.esriI