有两三天没更新博客了,感觉自己又偷懒啦!周五其实主要还是看了研究WorldWind中的Virtual Earth插件功能,我以后准备开发的跟VirtualEarth很类似的,一定得把它攻下。当时Virtual Earth学习没太有大的进展,遇到的难题是:1.图片如何叠加到球体上?(包括投影变换、定位、缩放等)2.Direct 3D开发 。因为Virtual Earth是在Blue Marble插件的基础上开发,所以周六我主要是看了Direct X的基础知识和研究Blue Marble。现在与大家一起分析一下BlueMarble。
Blue Marble插件实现主要是BMNG.cs文件,里面包括两个类BMNG类和BmngLoader类。
BMNG复杂实现BlueMarble功能的控制窗体,BMNG的意思是Blue Marble Next Generation。
BmngLoader类继承自Plugin类,是负责实现插件功能的。它重载了Plugin.cs的Load()和Unload()方法,实现了菜单项点击处理功能menuItemClicked()。这跟一般的实现插件功能的流程是一样的。
下面分别看一下,关键代码的实现。
BMNG类:主要是BMNG构造函数里的处理,这是实现图片叠加到球体上处理的关键,也是分析学习的重点。
{
//
// Required for Windows Form Designer support
//
InitializeComponent();
this.m_MenuItem = menuItem; // Plugin menu item ref
int lastSelectedDatasetIndex = 1;
try
{
using (StreamReader reader = new StreamReader(Path.GetDirectoryName(Application.ExecutablePath) + "\\Plugins\\BlueMarble\\settings.txt"))
{
lastSelectedDatasetIndex = int.Parse(reader.ReadLine().Trim());
}
}
catch
{
}
//设置Blue Marble类型
comboBoxBmngVersion.SelectedIndex = lastSelectedDatasetIndex;
m_WorldWindow = worldWindow;
m_RenderableList.ShowOnlyOneLayer = true;
bool foundImagesObject = false;
lock (m_WorldWindow.CurrentWorld.RenderableObjects.ChildObjects.SyncRoot)
{
foreach (WorldWind.Renderable.RenderableObject ro in m_WorldWindow.CurrentWorld.RenderableObjects.ChildObjects)
{
if (ro is WorldWind.Renderable.RenderableObjectList && (ro.Name == "Images")) // SF FIX: don't add to layers called 'xxxx images'!
{
WorldWind.Renderable.RenderableObjectList imagesList = ro as WorldWind.Renderable.RenderableObjectList;
imagesList.ChildObjects.Insert(0, m_RenderableList);
foundImagesObject = true;
break;
}
}
}
//如果没找到,直接在顶层添加图层列表m_RenderableList
if (!foundImagesObject)
{
m_WorldWindow.CurrentWorld.RenderableObjects.ChildObjects.Add(m_RenderableList);
}
//新建图层ImageLayer
m_BlueMarbleBase = new WorldWind.Renderable.ImageLayer(
"Blue Marble Base Image",
m_WorldWindow.CurrentWorld,
0,
null,
-90, 90, -180, 180, 1.0f, null);
m_BlueMarbleBase.ImageUrl = "http://worldwind.arc.nasa.gov/downloads/land_shallow_topo_2048.dds";
//新建NltImageStore对象,主要是辅助实现QuadTileSet(瓦片式图层)
WorldWind.NltImageStore ia = new WorldWind.NltImageStore("106", "http://nww.terraserver-usa.com/nwwtile.ashx");
ia.DataDirectory = null;
ia.LevelZeroTileSizeDegrees = 36.0;
ia.LevelCount = 4;
ia.ImageExtension = "jpg";
ia.CacheDirectory = String.Format("{0}\\Blue Marble", m_WorldWindow.Cache.CacheDirectory);
WorldWind.ImageStore[] ias = new WorldWind.ImageStore[1];
ias[0] = ia;
m_BlueMarbleTiled = new WorldWind.Renderable.QuadTileSet(
"Blue Marble Tiled",
m_WorldWindow.CurrentWorld,
0,
90, -90, -180, 180,
true,
ias);
m_BlueMarbleTiled.ServerLogoFilePath = Path.GetDirectoryName(Application.ExecutablePath) + "\\Data\\Icons\\Interface\\meatball.png";
//BlueMarble图层集合
m_BlueMarbleList.IsOn = false;
m_BlueMarbleList.Add(m_BlueMarbleBase);
m_BlueMarbleList.Add(m_BlueMarbleTiled);
m_ShadedList = new WorldWind.Renderable.RenderableObjectList("BMNG");
m_ShadedList.ShowOnlyOneLayer = true;
m_ShadedList.IsOn = false;
//12个月的影像图层
for (int i = 0; i < 12; i++)
{
m_ImageLayers[0, i] = new WorldWind.Renderable.ImageLayer(
String.Format("Base Image - {0}.2004", i + 1),
m_WorldWindow.CurrentWorld,
0,
null,
-90, 90, -180, 180, 1.0f, null);
m_ImageLayers[0, i].ImageUrl = String.Format("{0}world.topo.2004{1:D2}.jpg", m_BmngBaseImageUrl, i + 1);
WorldWind.NltImageStore imageStore = new WorldWind.NltImageStore(String.Format("bmng.topo.2004{0:D2}", i + 1), "http://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;
//构建QuadTileSet瓦片图层
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);
m_QuadTileLayers[0, i].ServerLogoFilePath = Path.GetDirectoryName(Application.ExecutablePath) + "\\Data\\Icons\\Interface\\meatball.png";
m_RenderableLayers[0, i] = new WorldWind.Renderable.RenderableObjectList(String.Format("{0}.2004", i + 1));
m_RenderableLayers[0, i].IsOn = false;
m_RenderableLayers[0, i].Add(m_ImageLayers[0, i]);
m_RenderableLayers[0, i].Add(m_QuadTileLayers[0, i]);
m_ShadedList.Add(m_RenderableLayers[0, i]);
}
m_ShadedBathyList = new WorldWind.Renderable.RenderableObjectList("BMNG (Bathymetry)");
m_ShadedBathyList.ShowOnlyOneLayer = true;
m_ShadedBathyList.IsOn = false;
for (int i = 0; i < 12; i++)
{
m_ImageLayers[1, i] = new WorldWind.Renderable.ImageLayer(
String.Format("Base Image - {0}.2004", i + 1),
m_WorldWindow.CurrentWorld,
0,
String.Format("{0}\\Data\\Earth\\BmngBathy\\world.topo.bathy.2004{1:D2}.jpg", Path.GetDirectoryName(Application.ExecutablePath), i + 1),
-90, 90, -180, 180, 1.0f, null);
// m_ImageLayers[1, i].ImageUrl = String.Format("{0}world.topo.bathy.2004{1:D2}.jpg", m_BmngBaseImageUrl, i+1);
//根据http://worldwind25.arc.nasa.gov/tile/tile.aspx构建瓦片式影像的URI地址
WorldWind.NltImageStore imageStore = new WorldWind.NltImageStore(String.Format("bmng.topo.bathy.2004{0:D2}", i + 1), "http://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 + Bathymetry) Tiled - {0}.2004", i + 1));
ias = new WorldWind.ImageStore[1];
ias[0] = imageStore;
//构建瓦片式影像
m_QuadTileLayers[1, i] = new WorldWind.Renderable.QuadTileSet(
String.Format("Tiled - {0}.2004", i + 1),
m_WorldWindow.CurrentWorld,
0,
90, -90, -180, 180, true, ias);
m_QuadTileLayers[0, i].ServerLogoFilePath = Path.GetDirectoryName(Application.ExecutablePath) + "\\Data\\Icons\\Interface\\meatball.png";
m_RenderableLayers[1, i] = new WorldWind.Renderable.RenderableObjectList(String.Format("{0}.2004", i + 1));
m_RenderableLayers[1, i].IsOn = false;
m_RenderableLayers[1, i].Add(m_ImageLayers[1, i]);
m_RenderableLayers[1, i].Add(m_QuadTileLayers[1, i]);
m_ShadedBathyList.Add(m_RenderableLayers[1, i]);
}
m_RenderableList.Add(m_BlueMarbleList);
m_RenderableList.Add(m_ShadedList);
m_RenderableList.Add(m_ShadedBathyList);
// m_RenderableList.Add(m_UnShadedList);
this.trackBarMonth.Value = System.DateTime.Now.Month - 1;
timer = new Timer();
timer.Interval = 1000;
timer.Tick += new EventHandler(timer_Tick);
timer.Start();
}
图3:
原143行 m_ImageLayers[0, i].ImageUrl = String.Format("{0}world.topo.2004{1:D2}.jpg", m_BmngBaseImageUrl, i + 1);从http://worldwind28.arc.nasa.gov/public/上加载图片,构建ImageLayer图层
原183行 String.Format("{0}\\Data\\Earth\\BmngBathy\\world.topo.bathy.2004{1:D2}.jpg", Path.GetDirectoryName(Application.ExecutablePath), i + 1),从文件系统中获取影像图片,构建ImageLayer图层。
另外,构造函数中有个定时器timer,实现每1秒,执行一次timer_Tick(object sender, EventArgs e)。
{
try
{
//Added TimeKeeper based Modis Month
//if (WorldWind.TimeKeeper.CurrentTimeUtc.Month != trackBarMonth.Value)
// trackBarMonth.Value = WorldWind.TimeKeeper.CurrentTimeUtc.Month-1;
if (m_CurrentMonth != trackBarMonth.Value)
{
TurnOffLayers();
for (int i = 0; i < 2; i++)
{
m_RenderableLayers[i, trackBarMonth.Value].IsOn = true;
}
m_CurrentMonth = trackBarMonth.Value;
this.statusBarMonth.Text = GetMonth(trackBarMonth.Value + 1) + " - 2004";
}
if (m_CurrentVersion != comboBoxBmngVersion.SelectedIndex)
{
m_CurrentVersion = comboBoxBmngVersion.SelectedIndex;
if (m_BlueMarbleList.IsOn)
m_BlueMarbleList.IsOn = false;
if (m_ShadedList.IsOn)
m_ShadedList.IsOn = false;
if (m_ShadedBathyList.IsOn)
m_ShadedBathyList.IsOn = false;
if (comboBoxBmngVersion.SelectedIndex == 2)//3)
{
// show blue marble (original)
m_BlueMarbleList.IsOn = true;
this.statusBarMonth.Text = "";
setMonthLabelsAndSlider(false);
}
else
{
setMonthLabelsAndSlider(true);
switch (comboBoxBmngVersion.SelectedIndex)
{
case 0:
m_ShadedList.IsOn = true;
break;
case 1:
m_ShadedBathyList.IsOn = true;
break;
// case 2:
// m_UnShadedList.IsOn = true;
// break;
default:
break;
}
}
}
}
catch
{ }
}
}
我个人认为,使用定时器处理,是有些浪费效率,但是主要是为了LayerManager管理里关闭/打开某个图层处理的。如果仅仅为了Blue Marble类型改变球体的底图,完全可以使用Combox的处理事件,就没必要用定时器处理。
BMNGLoader类,按一般插件重载实现Plugin.cs的Load() 、Unload()方法即可。这也是一般套路,无需多说。
{
if (ParentApplication.WorldWindow.CurrentWorld.Name.IndexOf("Earth") >= 0)
{
m_MenuItem = new MenuItem("Blue Marble");
m_MenuItem.Click += new EventHandler(menuItemClicked);
ParentApplication.PluginsMenu.MenuItems.Add(m_MenuItem);
m_BmngForm = new BMNG(ParentApplication.WorldWindow, m_MenuItem);
m_BmngForm.Owner = ParentApplication;
m_ToolbarItem = new WorldWind.WindowsControlMenuButton(
"NASA Blue Marble",
Path.GetDirectoryName(System.Windows.Forms.Application.ExecutablePath) + "\\Data\\Icons\\Interface\\bmng.png",
m_BmngForm);
//加载到WorldWindow.MenuBar菜单里
ParentApplication.WorldWindow.MenuBar.AddToolsMenuButton(m_ToolbarItem);
base.Load();
}
}
本系列其他部分:
WorldWind学习系列八:Load/Unload Plugins——直捣黄龙篇
WorldWind学习系列七:Load/Unload Plugins——投石问路篇
WorldWind学习系列四:功能分析——Show Planet Axis、Show Position 、Show Cross Hairs功能
WorldWind学习系列三:简单功能分析——主窗体的键盘监听处理及拷贝和粘贴位置坐标功能
WorldWind学习系列三:功能分析——截屏功能和“关于”窗体分析