“土耳其冰淇淋”通过精心收集,向本站投稿了6篇WorldWind系列十三:地形数据(DEM)加载和应用(入门篇),下面是小编为大家整理后的WorldWind系列十三:地形数据(DEM)加载和应用(入门篇),欢迎大家借鉴与参考,希望对大家有所帮助。

WorldWind系列十三:地形数据(DEM)加载和应用(入门篇)

篇1:WorldWind系列十三:地形数据(DEM)加载和应用(入门篇)

学习WW中DEM的加载和应用对于基于WW搞三维GIS插件开发很重要,我一直也想关注和搜集相关学习资料,之前没有开始研究它,主要是自己没能在 WW看到高程应用功能,感觉没法切入。我昨天下午和今天上午的时间就花在找研究切入点啦!实质上就是找如何在WW查看高程的功能。(现在回想一下,感觉浪费时间了,还是因为我对WW的功能没能完全知道)

我在开始学习DEM加载和应用前,我主要参看了CSDN上的博文——《庆贺,绘制出World Wind的LOD地形网格》(blog.csdn.net/paul_xj/archive//08/29/1763585.aspx),相信研究WW的人都应该看过这篇和他的三篇学习总结。我也看过研究过好多遍,结合自己的学习,每遍拜读都有些收获。他的文章关注度很高,绝对是WW初学者的起航灯塔。但是,我对上面提到的《绘制出World Wind的LOD地形网格》几乎要全文否定。我不希望网友以该文为标准来学习WW的地形网格(即DEM加载应用),因为我原本也是很相信他说的,按他说的去做的,事实不是那样的,相信他也是刚入门时写下的,很多DirectX的东西理解也不是很深刻的。

所谓的绘制出LOD地形网格有问题!怎么能将DirectX三维渲染与GIS中DEM混为一团?!地形图包括地物和地貌。而地貌主要体现在等高线上,在三维中体现为DEM应用(数字高程模型)!上面博客中讲到的构建地形网格只是将DirectX的面渲染改为线渲染,这还是平面级别的,根本没有体现高程,怎么就是地形网格啦?!从DirectX三维技术角度讲,也是有问题的:DX中面渲染和线渲染存储点的方式是不同的,因为WW中使用LOD模型各层要渲染的面很多且相邻,所以简单更改渲染方式没能看出影响。但如果你绘制单个面然后更改为线性渲染,就会看出问题啦!这还是平面级别上面,面渲染改为线渲染会出现问题,如果是三维物体(立方体)如果简单改为线渲染,问题更明显!(你学过DirectX后,试一下就知道啦!)

至于博客中说到的:“按下‘Ctrl+W’出来的网格,是什么空中网格”,只是简单地操作,放大后看到后面是“天空”,就认为是什么空中网格,这真是典型的形而上学啦!真正学习过三维开发的人都会知道:为了提高效率,三维渲染只是渲染可视的前一面,不被看到的面统统隐去(不渲染)。大家放大操作后看到“天空”而不是又一层网格,就是因为后面的网格不可视。‘Ctrl+W’出来的网格,是个空心球体框架。只是更改DX三维球体渲染时 FillMode而已。

WorldWindow.cs中响应“CRL+W”的处理代码:

1351行:

// Control key down
else if (e.Control)
{
switch (e.KeyCode)
{
case Keys.D:
this.showDiagnosticInfo = !this.showDiagnosticInfo;
return true;
case Keys.W:
renderWireFrame. = !renderWireFrame;
return true;
}
}

然后,看Render方法,代码行号776

// Set fill mode
if (renderWireFrame)
m_Device3d.RenderState.FillMode = FillMode.WireFrame;
else
m_Device3d.RenderState.FillMode = FillMode.Solid;

所以看到的是球体框架而不是实心球,

压根没有空中网格一说!

这是文章中最后的截图,貌似很像他所说的地形网格,可能有网友会据此驳斥我。首先,在上面我已经从DirectX渲染技术角度上,我已经说明了这不是地形格网。其次,图中能表现出地形的高低起伏,是因为有地表面渲染中有DEM数据的加载和应用(这是关键)。我认为地形网格其实很简单,将地表面的渲染FillMode = FillMode.WireFrame;(注意:不是球面渲染的FillMode)。

我一向主张学习WW要从功能入手,DEM学习也应该从功能入手,现在看看WW中体现DEM功能:(参看www.help2go.com/Tutorials/Software_Applications/View_the_world_with_NASA_Worldwind.html)

Zoom in further,and Worldwind will download higher resolution images,giving you greater and greater detail. Now,hold down the right-mouse button and move your mouse down a bit -- your view will rotate a bit,so rather than looking head-on at the city from above,you'll be looking at it from an angle.

按住鼠标右键,移动鼠标一点,你就可以从一个角度看地貌了,而不是垂直向下看啦。

可能很多网友已经知道如何看到上面的效果啦!可是我之前不知道,所以一直困惑没找到切入点。我的WW学习很强调从功能入手分析代码学习代码。

上面的分析,算是DEM加载和应用入门篇吧!因为我自己就是走过这个阶段的,希望你也有所收获。然后下一篇,我们从代码基本分析DEM数据的加载和使用。

另附:(需要了解的两个知识点)

LOD是Level of Detail的缩写,意为层次细节度,技术指根据物体模型的节点在显示环境中所处的位置和重要度,决定物体渲染的资源分配,降低非重要物体的面数和细节度,从而获得高效率的渲染运算。

DDS(DirectDraw Surface)文件格式是微软为DirectX开发的一种图片格式,它是可以使用类似S3TC标准提供的一种压缩纹理格式. DDS文件可以有很多不同的格式,可以含有 Mipmap 或不保存 Mipmap 信息,可以使用压缩或非压缩的像素格式,常见的压缩数据方式有 DXTn(DXT1~DXT5),DDS文件的结构见MSDN: DDS File Reference. 参看:www.csinx.org/IBLog/article.asp?id=8

篇2:WorldWind系列十四:DEM数据加载和应用――以SRTM为例(上)

DEM应用在WW的三维表现中占有很重要的位置,跟影像数据同等重要!幸好影像和DEM的加载和处理原理上几乎一致,对基于WW搞GIS三维开发来说是件好事,理解好任何一种,另一种触类旁通!前一篇,主要从功能上做了简单入门介绍,该篇将从代码级别分析WW内置的SRTM的DEM数据加载和应用,下一篇讲从二次开发角度上讲解如何处理、配置自己的影像和DEM数据,呵呵,因为DEM部分很重要,且是放假期间我也有时间,争取篇篇精彩!

两个缩写词介绍:因为这两个缩写词常出现,知道是什么缩写,就不觉得神秘啦!

SRTM:The Shuttle Radar Topography Mission (SRTM) obtained elevation data on a near-global scale to generate the most complete high-resolution digital topographic database of Earth. SRTM consisted of a specially modified radar system that flew onboard the Space Shuttle Endeavour during an 11-day mission in February of .

NLT:NASA Learning Technologies.

我从BMNG.cs为例入手研究DEM的使用,当然研究瓦片影像也该从此入手,但,今天影像不是我们关注的重点。现在正式步入主题,跟我一起分析和学习代码吧!

BMNG.cs类144行构造函数中代码,

WorldWind.NltImageStore imageStore = new WorldWind.NltImageStore(String.Format(“bmng.topo.{0:D2}”, i + 1), “worldwind25.arc.nasa.gov/tile/tile.aspx”);
imageStore.DataDirectory = null;
imageStore.LevelZeroTileSizeDegrees = 36.0;
imageStore.LevelCount = 5;
imageStore.ImageExtension = “jpg”;
imageStore.CacheDirectory = String.Format(“{0}BMNG{1}”, m_WorldWindow.Cache.CacheDirectory, String.Format(“BMNG (Shaded) Tiled - {0}.2004”, i + 1));
ias = new WorldWind.ImageStore[1];
ias[0] = imageStore;
m_QuadTileLayers[0, i] = new WorldWind.Renderable.QuadTileSet(
String.Format(“Tiled - {0}.2004”, i + 1),
m_WorldWindow.CurrentWorld,
0,
90, -90, -180, 180,
true,
ias);

BMNG中的NltImageStore.cs、QuadTileSet类。这是我们关注的对象。

QuadTileSet继承自RenderableObject,是要绘制渲染的对象类。

关注它的562行Update方法、517行Initialize()方法、 701行Render()方法。

Update()方法

QuadTileSet的Update()方法

public override void Update(DrawArgs drawArgs)
{
if (!isInitialized)
Initialize(drawArgs);
if (m_effectPath != null && m_effect == null)
{
string errs = string.Empty;
m_effect = Effect.FromFile(DrawArgs.Device, m_effectPath, null, “”, ShaderFlags.None, m_effectPool, out errs);
if (errs != null && errs != string.Empty)
{
Log.Write(Log.Levels.Warning, “Could not load effect ” + m_effectPath + “: ” + errs);
Log.Write(Log.Levels.Warning, “Effect has been disabled.”);
m_effectPath = null;
m_effect = null;
}
}
if (ImageStores[0].LevelZeroTileSizeDegrees < 180)
{
// Check for layer outside view
double vrd = DrawArgs.Camera.ViewRange.Degrees;
double latitudeMax = DrawArgs.Camera.Latitude.Degrees + vrd;
double latitudeMin = DrawArgs.Camera.Latitude.Degrees - vrd;
double longitudeMax = DrawArgs.Camera.Longitude.Degrees + vrd;
double longitudeMin = DrawArgs.Camera.Longitude.Degrees - vrd;
if (latitudeMax < m_south || latitudeMin > m_north || longitudeMax < m_west || longitudeMin > m_east)
return;
}
if (DrawArgs.Camera.ViewRange * 0.5f >
Angle.FromDegrees(TileDrawDistance * ImageStores[0].LevelZeroTileSizeDegrees))
{
lock (m_topmostTiles.SyncRoot)
{
foreach (QuadTile qt in m_topmostTiles.Values)
qt.Dispose();
m_topmostTiles.Clear();
ClearDownloadRequests();
}
return;
}
//知识点,可以看看,如何计算不可见瓦片的算法。
RemoveInvisibleTiles(DrawArgs.Camera);

下面主要是如何计算和加载瓦片式影像的,是重点,但不是这次的重点。

try
{
//根据Camera所对的中心经纬度,计算中心点的行列号
int middleRow = MathEngine.GetRowFromLatitude(DrawArgs.Camera.Latitude, ImageStores[0].LevelZeroTileSizeDegrees);
int middleCol = MathEngine.GetColFromLongitude(DrawArgs.Camera.Longitude, ImageStores[0].LevelZeroTileSizeDegrees);
//根据行列号,反推瓦片的四点对应的经度或纬度
double middleSouth = -90.0f + middleRow * ImageStores[0].LevelZeroTileSizeDegrees;
double middleNorth = -90.0f + middleRow * ImageStores[0].LevelZeroTileSizeDegrees + ImageStores[0].LevelZeroTileSizeDegrees;
double middleWest = -180.0f + middleCol * ImageStores[0].LevelZeroTileSizeDegrees;
double middleEast = -180.0f + middleCol * ImageStores[0].LevelZeroTileSizeDegrees + ImageStores[0].LevelZeroTileSizeDegrees;
double middleCenterLat = 0.5f * (middleNorth + middleSouth);
double middleCenterLon = 0.5f * (middleWest + middleEast);
//这里存在一个算法,由中心瓦片框,向四周扩散地找相邻的瓦片矩形框。
//有兴趣的网友可以看一下,根据算法画出图来就好理解啦。(我感觉该算法对以后开发会有用的)
int tileSpread = 4;
for (int i = 0; i < tileSpread; i++)
{
for (double j = middleCenterLat - i * ImageStores[0].LevelZeroTileSizeDegrees; j < middleCenterLat + i * ImageStores[0].LevelZeroTileSizeDegrees; j += ImageStores[0].LevelZeroTileSizeDegrees)
{
for (double k = middleCenterLon - i * ImageStores[0].LevelZeroTileSizeDegrees; k < middleCenterLon + i * ImageStores[0].LevelZeroTileSizeDegrees; k += ImageStores[0].LevelZeroTileSizeDegrees)
{
//根据经纬度和tileSize来计算行列号,这里LevelZeroTileSizeDegrees为第0层的瓦片大小为36度,瓦片总个数为50片
int curRow = MathEngine.GetRowFromLatitude(Angle.FromDegrees(j), ImageStores[0].LevelZeroTileSizeDegrees);
int curCol = MathEngine.GetColFromLongitude(Angle.FromDegrees(k), ImageStores[0].LevelZeroTileSizeDegrees);
long key = ((long)curRow << 32) + curCol;
//如果集合m_topmostTiles已经存在QuadTile,则更新QuadTile
QuadTile qt = (QuadTile)m_topmostTiles[key];
if (qt != null)
{
qt.Update(drawArgs);
continue;
}
// Check for tile outside layer boundaries,获取外边框四点经度或纬度坐标
double west = -180.0f + curCol * ImageStores[0].LevelZeroTileSizeDegrees;
if (west > m_east)
continue;
double east = west + ImageStores[0].LevelZeroTileSizeDegrees;
if (east < m_west)
continue;
double south = -90.0f + curRow * ImageStores[0].LevelZeroTileSizeDegrees;
if (south > m_north)
continue;
double north = south + ImageStores[0].LevelZeroTileSizeDegrees;
if (north < m_south)
continue;
//结合中不存在,创建新的QuadTile
qt = new QuadTile(south, north, west, east, 0, this);
//判断新的QuadTile是否在可视区域中。(可以关注一下:Intersects()方法判断矩形框相交)
if (DrawArgs.Camera.ViewFrustum.Intersects(qt.BoundingBox))
{
lock (m_topmostTiles.SyncRoot)
m_topmostTiles.Add(key, qt);
//调用QuadTile的Update()方法
qt.Update(drawArgs);
}
}
}
}
}
catch (System.Threading.ThreadAbortException)
{
}
catch (Exception caught)
{
Log.Write(caught);
}
}

Render()方法的关键代码为:

device.VertexFormat = CustomVertex.PositionNormalTextured.Format;
foreach (QuadTile qt in m_topmostTiles.Values)
qt.Render(drawArgs);

从上面可以看出,QuadTileSet可看作是QuadTile的集合,真正实现更新和渲染的是QuadTile对象。里面有影像的加载和渲染绘制,也有DEM的渲染绘制。

我们先看看QuadTile.cs 中Update()方法:

QuadTile的Update()代码

public virtual void Update(DrawArgs drawArgs)
{
if (m_isResetingCache)
return;
try
{
double tileSize = North - South;
if (!isInitialized)
{
if (DrawArgs.Camera.ViewRange * 0.5f < Angle.FromDegrees(QuadTileSet.TileDrawDistance * tileSize)
&& MathEngine.SphericalDistance(CenterLatitude, CenterLongitude,
DrawArgs.Camera.Latitude, DrawArgs.Camera.Longitude) < Angle.FromDegrees(QuadTileSet.TileDrawSpread * tileSize * 1.25f)
&& DrawArgs.Camera.ViewFrustum.Intersects(BoundingBox)
)
Initialize();
}
if (isInitialized && World.Settings.VerticalExaggeration != verticalExaggeration || m_CurrentOpacity != QuadTileSet.Opacity ||
QuadTileSet.RenderStruts != renderStruts)
{
//创建瓦片网格(重点)
CreateTileMesh();
}
if (isInitialized)
{
//判断进入下一层的条件(ViewRange角度、球面距离、可视区域)
if (DrawArgs.Camera.ViewRange < Angle.FromDegrees(QuadTileSet.TileDrawDistance * tileSize)
&& MathEngine.SphericalDistance(CenterLatitude, CenterLongitude,
DrawArgs.Camera.Latitude, DrawArgs.Camera.Longitude) < Angle.FromDegrees(QuadTileSet.TileDrawSpread * tileSize)
&& DrawArgs.Camera.ViewFrustum.Intersects(BoundingBox)
)
{
if (northEastChild == null || northWestChild == null || southEastChild == null || southWestChild == null)
{
//计算下一级别的四个子瓦片(重点,稍后一起看看)
ComputeChildren(drawArgs);
}
if (northEastChild != null)
{
northEastChild.Update(drawArgs);
}
if (northWestChild != null)
{
northWestChild.Update(drawArgs);
}
if (southEastChild != null)
{
southEastChild.Update(drawArgs);
}
if (southWestChild != null)
{
southWestChild.Update(drawArgs);
}
}
else
{
if (northWestChild != null)
{
northWestChild.Dispose();
northWestChild = null;
}
if (northEastChild != null)
{
northEastChild.Dispose();
northEastChild = null;
}
if (southEastChild != null)
{
southEastChild.Dispose();
southEastChild = null;
}
if (southWestChild != null)
{
southWestChild.Dispose();
southWestChild = null;
}
}
}
if (isInitialized)
{
if (DrawArgs.Camera.ViewRange / 2 > Angle.FromDegrees(QuadTileSet.TileDrawDistance * tileSize * 1.5f)
|| MathEngine.SphericalDistance(CenterLatitude, CenterLongitude, DrawArgs.Camera.Latitude, DrawArgs.Camera.Longitude) > Angle.FromDegrees(QuadTileSet.TileDrawSpread * tileSize * 1.5f))
{
if (Level != 0 || (Level == 0 && !QuadTileSet.AlwaysRenderBaseTiles))
this.Dispose();
}
}
}
catch
{
}
}

创建下一级的四个瓦片的方法:(可以被我们重用)

ComputeChildren(DrawArgs drawArgs)
public virtual void ComputeChildren(DrawArgs drawArgs)
{
//判断是否是最高级别
if (Level + 1 >= QuadTileSet.ImageStores[0].LevelCount)
return;
//计算瓦片的中点经纬度
double CenterLat = 0.5f * (South + North);
double CenterLon = 0.5f * (East + West);
if (northWestChild == null)
northWestChild = ComputeChild(CenterLat, North, West, CenterLon);
if (northEastChild == null)
northEastChild = ComputeChild(CenterLat, North, CenterLon, East);
if (southWestChild == null)
southWestChild = ComputeChild(South, CenterLat, West, CenterLon);
if (southEastChild == null)
southEastChild = ComputeChild(South, CenterLat, CenterLon, East);
}
ComputeChild(double childSouth, double childNorth, double childWest, double childEast)

///

/// Returns the QuadTile for specified location if available.

/// Tries to queue a download if not available.

///

/// 

private QuadTile ComputeChild(double childSouth, double childNorth, double childWest, double childEast)

{

QuadTile child = new QuadTile(

childSouth,

childNorth,

childWest,

childEast,

this.Level + 1,

QuadTileSet);

return child;

}

QuadTile.cs 中的CreateTileMesh()方法用来创建瓦片格网的,分别在Initialize() 、Update()方法中调用,

410行 这里调用的CreateElevatedMesh();是添加DEM数据创建高程格网的。

///

/// Builds flat or terrain mesh for current tile

///

public virtual void CreateTileMesh()

{

verticalExaggeration = World.Settings.VerticalExaggeration;

m_CurrentOpacity = QuadTileSet.Opacity;

renderStruts = QuadTileSet.RenderStruts;

if (QuadTileSet.TerrainMapped && Math.Abs(verticalExaggeration) > 1e-3)

//创建具有高程值的格网(今天要关注的)

CreateElevatedMesh();

else

//创建没有高程值的平面格网

CreateFlatMesh();

}

591行CreateElevatedMesh()

创建具有高程值的格网

///

/// Build the elevated terrain mesh

///

protected virtual void CreateElevatedMesh()

{

isDownloadingTerrain = true;

//vertexCountElevated值为40;向四周分别扩充一个样本点

// Get height data with one extra sample around the tile

double degreePerSample = LatitudeSpan / vertexCountElevated;

//获取具有高程值的TerrainTile对象(这是最关键部分,深入分析)

TerrainTile tile = QuadTileSet.World.TerrainAccessor.GetElevationArray(North + degreePerSample, South - degreePerSample, West - degreePerSample, East + degreePerSample, vertexCountElevated + 3);

float[,] heightData = tile.ElevationData;

int vertexCountElevatedPlus3 = vertexCountElevated / 2 + 3;

int totalVertexCount = vertexCountElevatedPlus3 * vertexCountElevatedPlus3;

northWestVertices = new CustomVertex.PositionNormalTextured[totalVertexCount];

southWestVertices = new CustomVertex.PositionNormalTextured[totalVertexCount];

northEastVertices = new CustomVertex.PositionNormalTextured[totalVertexCount];

southEastVertices = new CustomVertex.PositionNormalTextured[totalVertexCount];

double layerRadius = (double)QuadTileSet.LayerRadius;

// Calculate mesh base radius (bottom vertices)

// Find minimum elevation to account for possible bathymetry

float minimumElevation = float.MaxValue;

float maximumElevation = float.MinValue;

foreach (float height in heightData)

{

if (height < minimumElevation)

minimumElevation = height;

if (height > maximumElevation)

maximumElevation = height;

}

minimumElevation *= verticalExaggeration;

maximumElevation *= verticalExaggeration;

if (minimumElevation > maximumElevation)

{

// Compensate for negative vertical exaggeration

minimumElevation = maximumElevation;

maximumElevation = minimumElevation;

}

double verlap = 500 * verticalExaggeration; // 500m high tiles

// Radius of mesh bottom grid

meshBaseRadius = layerRadius + minimumElevation - overlap;

CreateElevatedMesh(ChildLocation.NorthWest, northWestVertices, meshBaseRadius, heightData);

CreateElevatedMesh(ChildLocation.SouthWest, southWestVertices, meshBaseRadius, heightData);

CreateElevatedMesh(ChildLocation.NorthEast, northEastVertices, meshBaseRadius, heightData);

CreateElevatedMesh(ChildLocation.SouthEast, southEastVertices, meshBaseRadius, heightData);

BoundingBox = new BoundingBox((float)South, (float)North, (float)West, (float)East,

(float)layerRadius, (float)layerRadius + 10000 * this.verticalExaggeration);

QuadTileSet.IsDownloadingElevation = false;

// Build common set of indexes for the 4 child meshes

int vertexCountElevatedPlus2 = vertexCountElevated / 2 + 2;

vertexIndexes = new short[2 * vertexCountElevatedPlus2 * vertexCountElevatedPlus2 * 3];

int elevated_idx = 0;

for (int i = 0; i < vertexCountElevatedPlus2; i++)

{

for (int j = 0; j < vertexCountElevatedPlus2; j++)

{

vertexIndexes[elevated_idx++] = (short)(i * vertexCountElevatedPlus3 + j);

vertexIndexes[elevated_idx++] = (short)((i + 1) * vertexCountElevatedPlus3 + j);

vertexIndexes[elevated_idx++] = (short)(i * vertexCountElevatedPlus3 + j + 1);

vertexIndexes[elevated_idx++] = (short)(i * vertexCountElevatedPlus3 + j + 1);

vertexIndexes[elevated_idx++] = (short)((i + 1) * vertexCountElevatedPlus3 + j);

vertexIndexes[elevated_idx++] = (short)((i + 1) * vertexCountElevatedPlus3 + j + 1);

}

}

calculate_normals(ref northWestVertices, vertexIndexes);

calculate_normals(ref southWestVertices, vertexIndexes);

calculate_normals(ref northEastVertices, vertexIndexes);

calculate_normals(ref southEastVertices, vertexIndexes);

isDownloadingTerrain = false;

}

596行TerrainTile tile = QuadTileSet.World.TerrainAccessor.GetElevationArray(North + degreePerSample, South - degreePerSample, West - degreePerSample, East + degreePerSample, vertexCountElevated + 3);

获取样本点的高程值数组。

WorldWind系列十三:地形数据(DEM)加载和应用(入门篇)

篇3:WorldWind系列十四:DEM数据加载和应用――以SRTM为例(下)

使用了TerrainAccessor.cs类120行代码

public virtual TerrainTile GetElevationArray(double north, double south, double west, double east, int samples)
{
TerrainTile res = null;
res = new TerrainTile(null);
res.North = north;
res.South = south;
res.West = west;
res.East = east;
res.SamplesPerTile = samples;
res.IsInitialized = true;
res.IsValid = true;
double latrange = Math.Abs(north - south);
double lonrange = Math.Abs(east - west);
float[,] data = new float[samples,samples];
float scaleFactor = (float)1/(samples - 1);
for(int x = 0; x < samples; x++)
{
for(int y = 0; y < samples; y++)
{
double curLat = north - scaleFactor * latrange * x;
double curLon = west + scaleFactor * lonrange * y;
//关键,获取瓦片所有样本点的高程值
data[x,y] = GetElevationAt(curLat, curLon, 0);
}
}
res.ElevationData = data;
return res;
}

关键代码:data[x,y] = GetElevationAt(curLat, curLon, 0);

GetElevationAt;在TerrainAccessor.cs是抽象方法,真正实现是在TerrainAccessor的子类NltTerrainAccessor中重载实现的。

120行  public override float GetElevationAt(double latitude, double longitude)
{
return GetElevationAt(latitude, longitude, m_terrainTileService.SamplesPerTile / m_terrainTileService.LevelZeroTileSizeDegrees);
}

TerrainAccessor对象哪里来的(即:在哪完成初始化和传入的?)

ConfigurationLoader.cs的Load()方法的97行代码:

TerrainAccessor[] terrainAccessor = getTerrainAccessorsFromXPathNodeIterator(worldIter.Current.Select(“TerrainAccessor”),
System.IO.Path.Combine(cache.CacheDirectory, worldName));
World newWorld = new World(
worldName,
new Microsoft.DirectX.Vector3(0, 0, 0),
new Microsoft.DirectX.Quaternion(0, 0, 0, 0),
equatorialRadius,
cache.CacheDirectory,
(terrainAccessor != null ? terrainAccessor[0] : null)//TODO: Oops, World should be able to handle an array of terrainAccessors
);

然后通过World对象传入到QuadTileSet类中的。

我们看看getTerrainAccessorsFromXPathNodeIterator方法如何完成完成TerrainAccessor对象。

注意:该方法返回值为TerrainAccessor[],是个数组,为什么呢??(请关注我下一篇文章)

2078行

getTerrainAccessorsFromXPathNodeIterator(XPathNodeIterator iter, string cacheDirectory)代码

private static TerrainAccessor[] getTerrainAccessorsFromXPathNodeIterator(XPathNodeIterator iter, string cacheDirectory)
{
System.Collections.ArrayList terrainAccessorList = new System.Collections.ArrayList();
//下面是读取DEM配置XML,并根据配置信息创建TerrainTileService对象和TerrainAccessor对象
while(iter.MoveNext())
{
string terrainAccessorName = iter.Current.GetAttribute(“Name”, “”);
if(terrainAccessorName == null)
{
// TODO: Throw exception?
continue;
}
XPathNodeIterator latLonBoxIter = iter.Current.Select(“LatLonBoundingBox”);
if(latLonBoxIter.Count != 1)
{
// TODO: Throw exception?
continue;
}
double north = 0;
double south = 0;
double west = 0;
double east = 0;
latLonBoxIter.MoveNext();
north = ParseDouble(getInnerTextFromFirstChild(latLonBoxIter.Current.Select(“North”)));
south = ParseDouble(getInnerTextFromFirstChild(latLonBoxIter.Current.Select(“South”)));
west = ParseDouble(getInnerTextFromFirstChild(latLonBoxIter.Current.Select(“West”)));
east = ParseDouble(getInnerTextFromFirstChild(latLonBoxIter.Current.Select(“East”)));
TerrainAccessor[] higerResolutionSubsets = getTerrainAccessorsFromXPathNodeIterator(
iter.Current.Select(“HigherResolutionSubsets”),
Path.Combine(cacheDirectory, terrainAccessorName));
XPathNodeIterator tileServiceIter = iter.Current.Select(“TerrainTileService”);
if(tileServiceIter.Count == 1)
{
string serverUrl = null;
string dataSetName = null;
double levelZeroTileSizeDegrees = double.NaN;
uint numberLevels = 0;
uint samplesPerTile = 0;
string dataFormat = null;
string fileExtension = null;
string compressionType = null;
tileServiceIter.MoveNext();
serverUrl = getInnerTextFromFirstChild(tileServiceIter.Current.Select(“ServerUrl”));
dataSetName = getInnerTextFromFirstChild(tileServiceIter.Current.Select(“DataSetName”));
levelZeroTileSizeDegrees = ParseDouble(getInnerTextFromFirstChild(tileServiceIter.Current.Select(“LevelZeroTileSizeDegrees”)));
numberLevels = uint.Parse(getInnerTextFromFirstChild(tileServiceIter.Current.Select(“NumberLevels”)));
samplesPerTile = uint.Parse(getInnerTextFromFirstChild(tileServiceIter.Current.Select(“SamplesPerTile”)));
dataFormat = getInnerTextFromFirstChild(tileServiceIter.Current.Select(“DataFormat”));
fileExtension = getInnerTextFromFirstChild(tileServiceIter.Current.Select(“FileExtension”));
compressionType = getInnerTextFromFirstChild(tileServiceIter.Current.Select(“CompressonType”));
//根据配置信息创建TerrainTileService对象和TerrainAccessor对象
TerrainTileService tts = new TerrainTileService(
serverUrl,
dataSetName,
levelZeroTileSizeDegrees,
(int)samplesPerTile,
fileExtension,
(int)numberLevels,
Path.Combine(cacheDirectory, terrainAccessorName),
World.Settings.TerrainTileRetryInterval,
dataFormat);
TerrainAccessor newTerrainAccessor = new NltTerrainAccessor(
terrainAccessorName,
west,
south,
east,
north,
tts,
higerResolutionSubsets);
terrainAccessorList.Add(newTerrainAccessor);
}
//TODO: Add Floating point terrain Accessor code
//TODO: Add WMSAccessor code and make it work in TerrainAccessor (which it currently doesn't)
}
if(terrainAccessorList.Count > 0)
{
return (TerrainAccessor[])terrainAccessorList.ToArray(typeof(TerrainAccessor));
}
else
{
return null;
}
}

再来看看DEM的配置在哪里和XML内容吧

路径:

C:Program FilesNASAWorld Wind 1.4ConfigEarth.xml

配置内容:

?xml version=“1.0” encoding=“UTF-8”?>

//黄色之间的XML就是一个TerrainAccessor配置

worldwind25.arc.nasa.gov/wwelevation/wwelevation.aspx

srtm30pluszip

20.0

12

150

Int16

bil

zip

90.0

-90.0

-180.0

180.0

接着上面的讲NltTerrainAccessor类76行代码GetElevationAt(double latitude, double longitude, double targetSamplesPerDegree)方法。

获取特定点的高程值

///

/// Get terrain elevation at specified location.

///

/// Latitude in decimal degrees.

/// Longitude in decimal degrees.

///

/// 

public override float GetElevationAt(double latitude, double longitude, double targetSamplesPerDegree)

{

try

{

if (m_terrainTileService == null || targetSamplesPerDegree < World.Settings.MinSamplesPerDegree)

return 0;

if (m_higherResolutionSubsets != null)

{

foreach (TerrainAccessor higherResSub in m_higherResolutionSubsets)

{

if (latitude > higherResSub.South && latitude < higherResSub.North &&

longitude > higherResSub.West && longitude < higherResSub.East)

{

return higherResSub.GetElevationAt(latitude, longitude, targetSamplesPerDegree);

}

}

}

//自己可以看看如何完成TerrainTile的初始化构建的

TerrainTile tt = m_terrainTileService.GetTerrainTile(latitude, longitude, targetSamplesPerDegree);

TerrainTileCacheEntry ttce = (TerrainTileCacheEntry)m_tileCache[tt.TerrainTileFilePath];

if (ttce == null)

{

ttce = new TerrainTileCacheEntry(tt);

AddToCache(ttce);

}

if (!ttce.TerrainTile.IsInitialized)

ttce.TerrainTile.Initialize();

ttce.LastAccess = DateTime.Now;

//获取高程值

return ttce.TerrainTile.GetElevationAt(latitude, longitude);

}

catch (Exception)

{

}

return 0;

}

上面获取高程值的关键:TerrainTile类的330行的GetElevationAt(double latitude, double longitude)方法

public float GetElevationAt(double latitude, double longitude)
{
try
{
double deltaLat = North - latitude;
double deltaLon = longitude - West;
//TileSizeDegrees为当前级别下瓦片的度数大小
//计算方法:158行tile.TileSizeDegrees = m_levelZeroTileSizeDegrees * Math.Pow(0.5, tile.TargetLevel);
//注意思考:SamplesPerTile-1为什么是减去1?传进来的SamplesPerTile=43而不是44?
//如果传入的是44,这里应该减2
double df2 = (SamplesPerTile-1) / TileSizeDegrees;
float lat_pixel = (float)(deltaLat * df2);
float lon_pixel = (float)(deltaLon * df2);
//这里是将点,近似成包含点的最小正方形(经纬度取整)
int lat_min = (int)lat_pixel;
int lat_max = (int)Math.Ceiling(lat_pixel);
int lon_min = (int)lon_pixel;
int lon_max = (int)Math.Ceiling(lon_pixel);
if(lat_min >= SamplesPerTile)
lat_min = SamplesPerTile - 1;
if(lat_max >= SamplesPerTile)
lat_max = SamplesPerTile - 1;
if(lon_min >= SamplesPerTile)
lon_min = SamplesPerTile - 1;
if(lon_max >= SamplesPerTile)
lon_max = SamplesPerTile - 1;
if(lat_min < 0)
lat_min = 0;
if(lat_max < 0)
lat_max = 0;
if(lon_min < 0)
lon_min = 0;
if(lon_max < 0)
lon_max = 0;
float delta = lat_pixel - lat_min;
//根据外矩形四顶点的经纬度分别插值计算中间线的高程
float westElevation =
ElevationData[lon_min, lat_min]*(1-delta) +
ElevationData[lon_min, lat_max]*delta;
float eastElevation =
ElevationData[lon_max, lat_min]*(1-delta) +
ElevationData[lon_max, lat_max]*delta;
//利用插值计算点的高程值
delta = lon_pixel - lon_min;
float interpolatedElevation =
westElevation*(1-delta) +
eastElevation*delta;
return interpolatedElevation;
}
catch
{
}
return 0;
}

public float[,] ElevationData就是存放当前瓦片所有样本点高程值的数值,

这是通过Initialize()中读取DEM(.bil)文件来获取的。

读取BIL文件高程数据

///

/// This method initializes the terrain tile add switches to

/// Initialize floating point/int 16 tiles

///

public void Initialize()

{

if(IsInitialized)

return;

if(!File.Exists(TerrainTileFilePath))

{

// Download elevation

if(request==null)

{

using( request = new TerrainDownloadRequest(this, m_owner, Row, Col, TargetLevel) )

{

request.SaveFilePath = TerrainTileFilePath;

request.DownloadInForeground();

}

}

}

if(ElevationData==null)

ElevationData = new float[SamplesPerTile, SamplesPerTile];

if(File.Exists(TerrainTileFilePath))

{

// Load elevation file

try

{

// TerrainDownloadRequest's FlagBadTile() creates empty files

// as a way to flag “bad” terrain tiles.

// Remove the empty 'flag' files after preset time.

try

{

FileInfo tileInfo = new FileInfo(TerrainTileFilePath);

if(tileInfo.Length == 0)

{

TimeSpan age = DateTime.Now.Subtract( tileInfo.LastWriteTime );

if(age < m_owner.TerrainTileRetryInterval)

{

// This tile is still flagged bad

IsInitialized = true;

}

else

{

// remove the empty 'flag' file

File.Delete(TerrainTileFilePath);

}

return;

}

}

catch

{

// Ignore any errors in the above block, and continue.

// For example, if someone had the empty 'flag' file

// open, the delete would fail.

}

//读取BIL文件数据的关键代码,可以被我们借鉴使用

using( Stream s = File.OpenRead(TerrainTileFilePath))

{

BinaryReader reader = new BinaryReader(s);

if(m_owner.DataType==“Int16”)

{

for(int y = 0; y < SamplesPerTile; y++)

for(int x = 0; x < SamplesPerTile; x++)

ElevationData[x,y] = reader.ReadInt16();

}

if(m_owner.DataType==“Float32”)

{

for(int y = 0; y < SamplesPerTile; y++)

for(int x = 0; x < SamplesPerTile; x++)

{

ElevationData[x,y] = reader.ReadSingle();

}

}

IsInitialized = true;

IsValid = true;

}

return;

}

catch(IOException)

{

// If there is an IO exception when reading the terrain tile,

// then either something is wrong with the file, or with

// access to the file, so try and remove it.

try

{

File.Delete(TerrainTileFilePath);

}

catch(Exception ex)

{

throw new ApplicationException(String.Format(“Error while trying to delete corrupt terrain tile {0}”, TerrainTileFilePath), ex);

}

}

catch(Exception ex)

{

// Some other type of error when reading the terrain tile.

throw new ApplicationException(String.Format(“Error while trying to read terrain tile {0}”, TerrainTileFilePath), ex);

}

}

}

另注:SRTM的高程数据存放路径如下图:(DEM跟影像也是分级存放的,存放方式一致)

至此,如何加载DEM数据创建网格的过程已经分析完了。

接下来,继续分析QuadTile.cs中CreateElevatedMesh()和Render方法,内容主要是DirectX编程,稍后添加……

篇4:WorldWind系列五:插件加载过程全解析

不得不承认World Wind的代码真的很庞大,没有太多帮助文档的前提下,一头钻进代码里肯定令你头疼的,甚至研究代码间关联仿佛是在走迷宫,我最近一直想弄明白如何在 MenuBar中加载那些插件的,WorldWind学习系列四中研究的只是特殊的三个功能加载的,那三个没有继承Plugin类,不算是插件功能加载。所以WorldWind学习系列四加载的三个是特殊情况,不是一般的插件加载。今天下午终于柳暗花明,如果你真正关注World Wind分析,那么就好好看看下面的插件加载过程全解析。

我们先看看Plugin类的继承图,看看到底都有些什么插件,然后在分析一般性的插件加载全过程。

哦,原来这么多插件,我们要基于WW开发自己的应用,只需继承Plugin类写出自己的插件功能即可的。

我们现在分析插件加载过程,请确保你看过WorldWind学习系列二:擒贼先擒王篇2 中的(5.加载上次使用的配置信息)。加载的插件入口就是WorldWind.cs的Main()中调用的LoadSettings()静态方法。

1.读取WorldWind的配置中插件信息

加载WorldWind配置

private static void LoadSettings()
{
try
{
//先读取上次使用时保存的“使用插件配置文件”,如果存在,则从文件中读取配置实例化WorldWindSettings
Settings = (WorldWindSettings) SettingsBase.Load(Settings, SettingsBase.LocationType.User);
if(!File.Exists(Settings.FileName))
{
//我们假定是配置文件不存在,这就是一个个地加载插件,保存到ArrayList中
Settings.PluginsLoadedOnStartup.Add(“ShapeFileInfoTool”);
//Settings.PluginsLoadedOnStartup.Add(“OverviewFormLoader”);
//Settings.PluginsLoadedOnStartup.Add(“Atmosphere”);
Settings.PluginsLoadedOnStartup.Add(“SkyGradient”);
Settings.PluginsLoadedOnStartup.Add(“BmngLoader”);
//Settings.PluginsLoadedOnStartup.Add(“Compass”);
//Settings.PluginsLoadedOnStartup.Add(“ExternalLayerManagerLoader”);
Settings.PluginsLoadedOnStartup.Add(“MeasureTool”);
//Settings.PluginsLoadedOnStartup.Add(“MovieRecorder”);
Settings.PluginsLoadedOnStartup.Add(“NRLWeatherLoader”);
Settings.PluginsLoadedOnStartup.Add(“ShapeFileLoader”);
Settings.PluginsLoadedOnStartup.Add(“Stars3D”);
Settings.PluginsLoadedOnStartup.Add(“GlobalClouds”);
Settings.PluginsLoadedOnStartup.Add(“PlaceFinderLoader”);
Settings.PluginsLoadedOnStartup.Add(“LightController”);
Settings.PluginsLoadedOnStartup.Add(“Earthquake_2.0.2.1”);
Settings.PluginsLoadedOnStartup.Add(“Historical_Earthquake_2.0.2.2”);
Settings.PluginsLoadedOnStartup.Add(“KMLImporter”);
//Settings.PluginsLoadedOnStartup.Add(“doublezoom”);
//Settings.PluginsLoadedOnStartup.Add(“PlanetaryRings”);
Settings.PluginsLoadedOnStartup.Add(“TimeController”);
//Settings.PluginsLoadedOnStartup.Add(“WavingFlags”);
Settings.PluginsLoadedOnStartup.Add(“ScaleBarLegend”);
Settings.PluginsLoadedOnStartup.Add(“Compass3D”);
Settings.PluginsLoadedOnStartup.Add(“AnaglyphStereo”);
Settings.PluginsLoadedOnStartup.Add(“GlobeIcon”);
}
// decrypt encoded user credentials
DataProtector dp = new DataProtector(DataProtector.Store.USE_USER_STORE);
if(Settings.ProxyUsername.Length > 0) Settings.ProxyUsername = dp.TransparentDecrypt(Settings.ProxyUsername);
if(Settings.ProxyPassword.Length > 0) Settings.ProxyPassword = dp.TransparentDecrypt(Settings.ProxyPassword);
}
catch(Exception caught)
{
Log.Write(caught);
}
}

2.Main()中后面调用MainApplication()方法,该MainApplication()调用 OpenStartupWorld(),用来初始化启动World对象。OpenStartupWorld()方法首先确定加载的是Earth/Moon 等,然后开始加载一个星球。

加载一个星球代码

///

/// Loads a new planet

///

private void OpenWorld(string worldXmlFile)

{

if(this.worldWindow.CurrentWorld != null)

{

try

{

this.worldWindow.ResetToolbar();

}

catch

{}

try

{

foreach(PluginInfo p in this.compiler.Plugins)

{

try

{

if(p.Plugin.IsLoaded)

p.Plugin.Unload();

}

catch

{}

}

}

catch

{}

try

{

this.worldWindow.CurrentWorld.Dispose();

}

catch

{}

}

if(this.gotoDialog != null)

{

this.gotoDialog.Dispose();

this.gotoDialog = null;

}

if(this.rapidFireModisManager != null)

{

this.rapidFireModisManager.Dispose();

this.rapidFireModisManager = null;

}

if(this.animatedEarthMananger != null)

{

this.animatedEarthMananger.Dispose();

this.animatedEarthMananger = null;

}

if(this.wmsBrowser != null)

{

this.wmsBrowser.Dispose();

this.wmsBrowser = null;

}

worldWindow.CurrentWorld = WorldWind.ConfigurationLoader.Load(worldXmlFile, worldWindow.Cache);

this.splashScreen.SetText(“Initializing menus...”);

// InitializePluginCompiler()是我们要关注的初始化插件编辑器PluginCompiler(真正管理加载插件的类)

InitializePluginCompiler();

//RenderableObject系统很复杂,我们稍后准备专门分析一下

foreach(RenderableObject worldRootObject in this.worldWindow.CurrentWorld.RenderableObjects.ChildObjects)

{

this.AddLayerMenuButtons(this.worldWindow, worldRootObject);

}

//加载Earth专有的MenuButton( AnimatedEarthManager和RapidFireModisManager)的配置,

//这里主要不关注

this.AddInternalPluginMenuButtons();

this.menuItemModisHotSpots.Enabled = worldWindow.CurrentWorld.IsEarth;

this.menuItemAnimatedEarth.Enabled = worldWindow.CurrentWorld.IsEarth;

}

3.我们看看InitializePluginCompiler()如何加载插件的?

初始化PluginCompiler

///

/// Compile and run plug-in “scripts”

///

private void InitializePluginCompiler()

{

Log.Write(Log.Levels.Debug, “CONF”, “initializing plugin compiler...”);

this.splashScreen.SetText(“Initializing plugins...”);

string pluginRoot = Path.Combine(DirectoryPath, “Plugins”);

compiler = new PluginCompiler(this, pluginRoot);

//#if DEBUG

// Search for plugins in worldwind.exe (plugin development/debugging aid)

compiler.FindPlugins(Assembly.GetExecutingAssembly());

//#endif

//从启动文件夹下Plugins文件夹,加载插件

compiler.FindPlugins();

//加载启动插件插件

compiler.LoadStartupPlugins();

}

4.加载启动插件函数的代码

加载启动插件

/// Loads the plugins that are set for load on world wind startup.
///
public void LoadStartupPlugins()
{
foreach(PluginInfo pi in m_plugins)
{
if(pi.IsLoadedAtStartup)
{
try
{
// Compile
Log.Write(Log.Levels.Debug, LogCategory, “loading ”+pi.Name+“ ...”);
worldWind.SplashScreen.SetText(“Initializing plugin ” + pi.Name);
//Load方法加载插件,Plugin类的Load()为虚方法,实质上调用的是各个插件重载后的Load()方法
Load(pi);
}
catch(Exception caught)
{
// Plugin failed to load
string message = “Plugin ” + pi.Name + “ failed: ” + caught.Message;
Log.Write(Log.Levels.Error, LogCategory, message);
Log.Write(caught);
// Disable automatic load of this plugin on startup
pi.IsLoadedAtStartup = false;
worldWind.SplashScreen.SetError(message);
}
}
}
}

5.现在看两个插件类重载的Load()方法的实例。例如:Compass3D.cs和TimeController.cs。

Compass3D.cs的Load()方法

重载后的Load方法

public override void Load()
{
m_menuItem = new System.Windows.Forms.MenuItem(“Compass”);
m_menuItem.Click += new EventHandler(m_menuItem_Click);
m_menuItem.Checked = World.Settings.ShowCompass;
ParentApplication.ToolsMenu.MenuItems.Add(m_menuItem);
m_form. = new FormWidget(“Compass”);
m_form.ClientSize = new System.Drawing.Size(200, 200);
m_form.Location = new System.Drawing.Point(0, 400);
m_form.BackgroundColor = World.Settings.WidgetBackgroundColor;
m_form.AutoHideHeader = true;
m_form.VerticalScrollbarEnabled = false;
m_form.HorizontalScrollbarEnabled = false;
m_form.BorderEnabled = false;
//注册两个窗体事件
m_form.OnResizeEvent += new FormWidget.ResizeHandler(m_form_OnResizeEvent);
m_form.OnVisibleChanged += new VisibleChangedHandler(m_form_OnVisibleChanged);
//实例化Compass3DWidget
m_compass = new Compass3DWidget();
m_compass.Location = new System.Drawing.Point(5, 0);
m_compass.Font = new System.Drawing.Font(“Ariel”, 10.0f, System.Drawing.FontStyle.Bold);
m_compass.ParentWidget = m_form;
m_form_OnResizeEvent(m_form, m_form.WidgetSize);
m_form.ChildWidgets.Add(m_compass);
m_form.Visible = World.Settings.ShowCompass;
//将要绘制的Widget加载到DrawArgs.NewRootWidget.ChildWidgets,为了在WorldWindow.cs的Render()方法里渲染
DrawArgs.NewRootWidget.ChildWidgets.Add(m_form);
//Compass 3D工具菜单按钮
m_toolbarItem = new WorldWind.NewWidgets.WidgetMenuButton(
“Compass 3D”,
basePath + “DataIconsInterfacecompass2.png”,
m_form);
//向MenuBar中添加Compass 3D菜单按钮
ParentApplication.WorldWindow.MenuBar.AddToolsMenuButton(m_toolbarItem);
base.Load();
}

TimeController.cs的Load方法,如下:

时间控制

public override void Load()
{
try
{
m_window = new WorldWind.NewWidgets.FormWidget(“Time Control”);
m_window.Name = “Time Control”;
m_window.ClientSize = new System.Drawing.Size(300, 183);
// Bug in FormWidget required anchor to be set before Location and parent widget
m_window.Anchor = WorldWind.NewWidgets.WidgetEnums.AnchorStyles.Left | WorldWind.NewWidgets.WidgetEnums.AnchorStyles.Bottom;
m_window.ParentWidget = DrawArgs.NewRootWidget;
m_window.Location = new System.Drawing.Point(0, DrawArgs.NewRootWidget.ClientSize.Height - 183);
m_window.Text = “Double Click to Re-Open”;
m_window.BorderEnabled = false;
m_window.AutoHideHeader = true;
m_window.BackgroundColor = System.Drawing.Color.FromArgb(0, 0, 0, 0);
m_window.HeaderEnabled = true;
m_window.Visible = false;
time = new WorldWind.NewWidgets.PictureBox();
time.Name = “Time”;
time.ImageUri = basePath + “DataIconsTimetime off.png”;
time.ClientLocation = new System.Drawing.Point(12, 59);
time.ClientSize = new System.Drawing.Size(42, 42);
time.Visible = true;
time.ParentWidget = m_window;
time.OnMouseEnterEvent += new EventHandler(time_OnMouseEnterEvent);
time.OnMouseLeaveEvent += new EventHandler(time_OnMouseLeaveEvent);
time.OnMouseUpEvent += new MouseEventHandler(time_OnMouseUpEvent);
time.CountHeight = false;
time.CountWidth = true;
m_window.ChildWidgets.Add(time);
play = new WorldWind.NewWidgets.PictureBox();
play.Name = “Play”;
if (TimeKeeper.Enabled)
{
play.ImageUri = basePath + “DataIconsTimeplay on.png”;
}
else
{
play.ImageUri = basePath + “DataIconsTimeplay off.png”;
}
play.ClientLocation = new System.Drawing.Point(50, 1);
play.ClientSize = new System.Drawing.Size(82, 82);
play.Visible = true;
play.ParentWidget = m_window;
play.OnMouseEnterEvent += new EventHandler(play_OnMouseEnterEvent);
play.OnMouseLeaveEvent += new EventHandler(play_OnMouseLeaveEvent);
play.OnMouseUpEvent += new System.Windows.Forms.MouseEventHandler(play_OnMouseUpEvent);
play.CountHeight = true;
play.CountWidth = true;
m_window.ChildWidgets.Add(play);
close = new WorldWind.NewWidgets.PictureBox();
close.Name = “Close”;
close.ImageUri = basePath + “DataIconsTimeclose off.png”;
close.ClientLocation = new System.Drawing.Point(29, 3);
close.ClientSize = new System.Drawing.Size(22, 22);
close.Visible = true;
close.ParentWidget = m_window;
close.OnMouseEnterEvent += new EventHandler(close_OnMouseEnterEvent);
close.OnMouseLeaveEvent += new EventHandler(close_OnMouseLeaveEvent);
close.OnMouseUpEvent += new MouseEventHandler(close_OnMouseUpEvent);
close.CountHeight = false;
close.CountWidth = false;
m_window.ChildWidgets.Add(close);
rewind = new WorldWind.NewWidgets.PictureBox();
rewind.Name = “Rewind”;
rewind.ImageUri = basePath + “DataIconsTimerepeat off.png”;
rewind.ClientLocation = new System.Drawing.Point(16, 26);
rewind.ClientSize = new System.Drawing.Size(32, 32);
rewind.Visible = true;
rewind.ParentWidget = m_window;
rewind.OnMouseEnterEvent += new EventHandler(rewind_OnMouseEnterEvent);
rewind.OnMouseLeaveEvent += new EventHandler(rewind_OnMouseLeaveEvent);
rewind.OnMouseUpEvent += new MouseEventHandler(rewind_OnMouseUpEvent);
rewind.CountHeight = false;
rewind.CountWidth = false;
m_window.ChildWidgets.Add(rewind);
pause = new WorldWind.NewWidgets.PictureBox();
pause.Name = “Pause”;
if (TimeKeeper.Enabled)
{
pause.ImageUri = basePath + “DataIconsTimepause off.png”;
}
else
{
pause.ImageUri = basePath + “DataIconsTimepause on.png”;
}
pause.ClientLocation = new System.Drawing.Point(35, 88);
pause.ClientSize = new System.Drawing.Size(64, 64);
pause.Visible = true;
pause.ParentWidget = m_window;
pause.OnMouseEnterEvent += new EventHandler(pause_OnMouseEnterEvent);
pause.OnMouseLeaveEvent += new EventHandler(pause_OnMouseLeaveEvent);
pause.OnMouseUpEvent += new System.Windows.Forms.MouseEventHandler(pause_OnMouseUpEvent);
pause.CountHeight = true;
pause.CountWidth = false;
m_window.ChildWidgets.Add(pause);
slow = new WorldWind.NewWidgets.PictureBox();
slow.Name = “Slow”;
slow.ImageUri = basePath + “DataIconsTimeslow off.png”;
slow.ClientLocation = new System.Drawing.Point(97, 88);
slow.ClientSize = new System.Drawing.Size(64, 64);
slow.Visible = true;
slow.ParentWidget = m_window;
slow.OnMouseEnterEvent += new EventHandler(slow_OnMouseEnterEvent);
slow.OnMouseLeaveEvent += new EventHandler(slow_OnMouseLeaveEvent);
slow.OnMouseUpEvent += new System.Windows.Forms.MouseEventHandler(slow_OnMouseUpEvent);
slow.CountHeight = false;
slow.CountWidth = false;
m_window.ChildWidgets.Add(slow);
fast = new WorldWind.NewWidgets.PictureBox();
fast.Name = “Fast”;
fast.ImageUri = basePath + “DataIconsTimefast off.png”;
fast.ClientLocation = new System.Drawing.Point(158, 88);
fast.ClientSize = new System.Drawing.Size(64, 64);
fast.Visible = true;
fast.ParentWidget = m_window;
fast.OnMouseEnterEvent += new EventHandler(fast_OnMouseEnterEvent);
fast.OnMouseLeaveEvent += new EventHandler(fast_OnMouseLeaveEvent);
fast.OnMouseUpEvent += new System.Windows.Forms.MouseEventHandler(fast_OnMouseUpEvent);
fast.CountHeight = false;
fast.CountWidth = false;
m_window.ChildWidgets.Add(fast);
timeLabel = new WorldWind.NewWidgets.TextLabel();
timeLabel.Name = “Current Time”;
timeLabel.ClientLocation = new System.Drawing.Point(134, 65);
timeLabel.ClientSize = new System.Drawing.Size(140, 60);
timeLabel.Visible = true;
timeLabel.ParentWidget = m_window;
timeLabel.Text = GetCurrentTimeString();
TimeKeeper.Elapsed += new System.Timers.ElapsedEventHandler(TimeKeeper_Elapsed);
timeLabel.CountHeight = false;
timeLabel.CountWidth = true;
timeLabel.WordBreak = true;
m_window.ChildWidgets.Add(timeLabel);
DrawArgs.NewRootWidget.ChildWidgets.Add(m_window);
m_toolbarItem = new WorldWind.NewWidgets.WidgetMenuButton(
“Time Controller”,
basePath + “DataIconsTimetime off.png”,
m_window);
//向MenuBar中添加菜单按钮
ParentApplication.WorldWindow.MenuBar.AddToolsMenuButton(m_toolbarItem);
}
catch (Exception ex)
{
Log.Write(ex);
}
base.Load();
}

其他插件的重载后的Load()方法也是类似的,不再赘述,

希望能帮研究WW的朋友理清插件加载过程的思路,少走弯路。

篇5:ArcSDE在数据连库接加载中的应用

ArcSDE在数据连库接加载中的应用

ArcSDE是ArcGIS与关系数据库之间的GIS通道.它允许用户在多种数据管理系统中管理地理信息,并使所有的ArcGIS应用程序都能够使用这些数据.ArcSDE是多用户ArcGIS系统的一个关键部件.它为DBMS提供了一个开放的接口,允许ArcGIS在多种数据库平台上管理地理信息.这些平台包括如果你的ArcGIS需要使用一个可以被大量用户同步访问并编辑的大型数据库,ArcSDE为你提供必要的.功能.通过ArcSDE你的ArcGIS可以在DBMS中轻而易举地管理一个共享的、多用户的空间数据库.

作 者:王重阳 张韶华 WANG Chong-yang ZHANG Shao-hua  作者单位:王重阳,WANG Chong-yang(北京北大方正软件技术学院,廊坊,065001)

张韶华,ZHANG Shao-hua(北京市测绘设计研究院,北京,100038)

刊 名:北京测绘 英文刊名:BEIJING SURVEYING AND MAPPING 年,卷(期): “”(2) 分类号:P208 关键词:ArcSDE   ArcGIS   安装   数据连接   数据加载  

篇6:DEM地形分析在山区地质灾害研究中的应用-以云南省漾濞县为例

DEM地形分析在山区地质灾害研究中的应用-以云南省漾濞县为例

摘要:云南省漾濞县具有典型的山地特点,每年其境内发生的地质灾害都给人民生命和财产造成了极大的损失.在漾濞县的地质灾害调查中,通过“3S”技术的应用,建立了漾濞县的数字高程模型,进行了基于ArcGIS的.地形分析,提取出了坡度和坡向等重要的地形因子.通过研究发现:坡度是漾濞县地质灾害频发的最主要控制因素,漾濞江及其支流上游的滑坡和崩塌为泥石流的发生提供了物源基础;同时阳坡是地质灾害发生的主要坡向,滑坡和崩塌等灾害发生频繁.最后,制作了漾濞县坡度、坡向分析图,指出了漾濞县较易发生地质灾害的地区,为其他山地地区地质灾害研究提供了一种借鉴模式.作 者:郑著彬    任静丽    ZHENG Zhu-bin    REN Jing-li  作者单位:郑著彬,ZHENG Zhu-bin(赣南师范学院,历史文化与旅游学院,江西,赣州,341000)

任静丽,REN Jing-li(赣南师范学院教育科学学院,江西,赣州,341000)

期 刊:云南地理环境研究   Journal:YUNNAN GEOGRAPHIC ENVIRONMENT RESEARCH 年,卷(期):2010, 22(2) 分类号:X87 关键词:DEM    地形分析    地质灾害    漾濞县
阅读剩余 0%
本站所有文章资讯、展示的图片素材等内容均为注册用户上传(部分报媒/平媒内容转载自网络合作媒体),仅供学习参考。 用户通过本站上传、发布的任何内容的知识产权归属用户或原始著作权人所有。如有侵犯您的版权,请联系我们反馈本站将在三个工作日内改正。