一、准备
Google Earth提供了个人免费版、Plus版、Pro版,个人开发只安装个人免费版就可以了,如果需要更多的功能,那么只有每年上交$400购买专业版了
到目前为止,GoogleEarth的二次开发接口还比较少,功能太弱,仅仅提供了1.0的类库。
GoogleEarth COM API参考文档可以在这里找到:
C#调用COM的参考资料多如牛毛,大家可以到网上搜一下
二、例子
这里提供一个利用VS2008 + Google Earth 5.0开发一个“Hello world”程序
首先,确保已经正确安装GE,打开VS2008 ,新建一个Windows应用程序项目,在“项目”菜单中选择“添加引用…”,切换到“COM”选项卡,选择“Google Earth 1.0 Type Library”,其实就是Google Earth的主程序
在项目的引用中你可以看到已经添加了一个EARTHLib的引用,然后我们就可以调用其中的接口进行开发了。
下面就是小例子的代码,打开GE,然后让GE保存一张截图,然后可以打开这个截图。
1.// 功能:GE实例 2: // 描述:GE COM API 网址:http://earth.google.com/comapi/index.html 3: // 作者:温伟鹏 4: // 日期:2008-01-20 5: 6: using System; 7: using System.Collections.Generic; 8: using System.ComponentModel; 9: using System.Data; 10: using System.Drawing; 11: using System.Text; 12: using System.Windows.Forms; 13: using EARTHLib; 14: using System.Runtime.InteropServices; 15: using System.IO; 16: using System.Diagnostics; 17: 18: namespace GEDemo 19: { 20: public partial class Form1 : Form 21: { 22: ///23: /// 标记GE是否已经启动 24: /// 25: private bool isGeStarted = false; 26: ///27: /// 定义GE应用程序类 28: /// 29: private ApplicationGEClass GeApp; 30: 31: public Form1() 32: { 33: InitializeComponent(); 34: } 35: 36: private void button1_Click(object sender, EventArgs e) 37: { 38: StartGE(); 39: } 40: 41: ///42: /// 启动GE 43: /// 44: private void StartGE() 45: { 46: if (isGeStarted) 47: { 48: return; 49: } 50: 51: try 52: { 53: GeApp = (ApplicationGEClass)Marshal.GetActiveObject("GoogleEarth.Application"); 54: 55: isGeStarted = true; 56: } 57: catch 58: { 59: GeApp = new ApplicationGEClass(); 60: 61: isGeStarted = true; 62: } 63: } 64: 65: private void button2_Click(object sender, EventArgs e) 66: { 67: string ssFile = Path.Combine(Application.StartupPath, "ScreenShot.jpg"); 68: 69: try 70: { 71: //quality的取值范围在(0,100)之间,质量越高,quality越大 72: GeApp.SaveScreenShot(ssFile, 100); 73: 74: MessageBox.Show("成功保存截屏图像:" + ssFile); 75: } 76: catch(Exception ex) 77: { 78: MessageBox.Show("保存截屏图像时发生错误:" + ex.Message); 79: } 80: } 81: 82: private void button3_Click(object sender, EventArgs e) 83: { 84: string ssFile = Path.Combine(Application.StartupPath, "ScreenShot.jpg"); 85: 86: if (!File.Exists(ssFile)) 87: { 88: MessageBox.Show("未能找到保存的截屏图像!"); 89: return; 90: } 91: 92: Process.Start(ssFile); 93: } 94: 95: private void button4_Click(object sender, EventArgs e) 96: { 97: this.Close(); 98: Application.Exit(); 99: } 100: 101: } 102: }
将GoogleEarth的界面隐藏掉,并将GoogleEarth的地图显示在自定义的窗体上。
1。主窗口代码
1: // 功能:GE实例(二) 2: // 描述:GE COM API 网址:http://earth.google.com/comapi/index.html 3: // 作者:温伟鹏 4: // 日期:2009-02-08 5: 6: using System; 7: using System.Collections.Generic; 8: using System.ComponentModel; 9: using System.Data; 10: using System.Drawing; 11: using System.Text; 12: using System.Windows.Forms; 13: using EARTHLib; 14: 15: namespace GEDemo 16: { 17: public partial class Form2 : Form 18: { 19: ///20: /// 用来关闭GoogleEarth的消息定义 21: /// 22: static readonly Int32 WM_QUIT = 0x0012; 23: 24: private IntPtr GEHWnd = (IntPtr)5; 25: private IntPtr GEHrender = (IntPtr)5; 26: private IntPtr GEParentHrender = (IntPtr)5; 27: ///28: /// 定义GE应用程序类 29: /// 30: private ApplicationGEClass GeApp; 31: 32: public Form2() 33: { 34: InitializeComponent(); 35: } 36: 37: protected override void OnLoad(EventArgs e) 38: { 39: base.OnLoad(e); 40: 41: if (!this.DesignMode) 42: { 43: GeApp = new ApplicationGEClass(); 44: 45: GEHWnd = (IntPtr)GeApp.GetMainHwnd(); 46: 47: NativeMethods.SetWindowPos(GEHWnd, NativeMethods.HWND_BOTTOM, 0, 0, 0, 0, 48: NativeMethods.SWP_NOSIZE + NativeMethods.SWP_HIDEWINDOW); 49: 50: GEHrender = (IntPtr)GeApp.GetRenderHwnd(); 51: GEParentHrender = (IntPtr)NativeMethods.GetParent(GEHrender); 52: 53: NativeMethods.MoveWindow(GEHrender, 0, 0, this.Width, this.Height, true); 54: 55: NativeMethods.SetParent(GEHrender, this.Handle); 56: } 57: } 58: 59: protected override void OnClosing(CancelEventArgs e) 60: { 61: base.OnClosing(e); 62: 63: NativeMethods.PostMessage(GeApp.GetMainHwnd(), WM_QUIT, 0, 0); 64: } 65: } 66: }
2、NativeMethods类定义:
1: // 功能:Windows API调用 2: // 描述:大家可以参照MSDN 3: // 作者:温伟鹏 4: // 日期:2009-02-08 5: 6: using System; 7: using System.Collections.Generic; 8: using System.Text; 9: using System.Runtime.InteropServices; 10: 11: namespace GEDemo 12: { 13: public class NativeMethods 14: { 15: [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)] 16: public static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int x, int y, int cx, int cy, UInt32 uflags); 17: 18: [DllImport("user32.dll", CharSet = CharSet.Auto)] 19: public static extern IntPtr PostMessage(int hWnd, int msg, int wParam, int lParam); 20: 21: #region 预定义 22: 23: public static readonly IntPtr HWND_BOTTOM = new IntPtr(1); 24: public static readonly IntPtr HWND_NOTOPMOST = new IntPtr(-2); 25: public static readonly IntPtr HWND_TOP = new IntPtr(0); 26: public static readonly IntPtr HWND_TOPMOST = new IntPtr(-1); 27: public static readonly UInt32 SWP_NOSIZE = 1; 28: public static readonly UInt32 SWP_NOMOVE = 2; 29: public static readonly UInt32 SWP_NOZORDER = 4; 30: public static readonly UInt32 SWP_NOREDRAW = 8; 31: public static readonly UInt32 SWP_NOACTIVATE = 16; 32: public static readonly UInt32 SWP_FRAMECHANGED = 32; 33: public static readonly UInt32 SWP_SHOWWINDOW = 64; 34: public static readonly UInt32 SWP_HIDEWINDOW = 128; 35: public static readonly UInt32 SWP_NOCOPYBITS = 256; 36: public static readonly UInt32 SWP_NOOWNERZORDER = 512; 37: public static readonly UInt32 SWP_NOSENDCHANGING = 1024; 38: 39: #endregion 40: 41: public delegate int EnumWindowsProc(IntPtr hwnd, int lParam); 42: 43: [DllImport("user32", CharSet = CharSet.Auto)] 44: public extern static IntPtr GetParent(IntPtr hWnd); 45: 46: [DllImport("user32", CharSet = CharSet.Auto)] 47: public extern static bool MoveWindow(IntPtr hWnd, int X, int Y, int nWidth, int nHeight, bool bRepaint); 48: 49: [DllImport("user32", CharSet = CharSet.Auto)] 50: public extern static IntPtr SetParent(IntPtr hWndChild, IntPtr hWndNewParent); 51: 52: [DllImport("user32.dll", ExactSpelling = true, CharSet = CharSet.Auto)] 53: public static extern IntPtr GetWindow(IntPtr hWnd, int uCmd); 54: 55: public static int GW_CHILD = 5; 56: public static int GW_HWNDNEXT = 2; 57: } 58: }
3、执行效果:
主要改进了三个方面。
1) 实现GoogleEarth显示画面随窗口大小改变而改变
2) 截获GoogleEarth鼠标消息,实现单击、双击功能;鼠标滚轮缩放现在只能放大!O(∩_∩)O~
3) 实现GoogleEarth彩色截图(测试环境:Windows 2003 Server ,Vista与Win7中不可用,XP未测)
下面还是继续看代码:
1、GoogleEarth动态改变大小
1: ///2: /// 重新改变GoogleEarth视图的大小 3: /// 4: private void ResizeGoogleControl() 5: { 6: NativeMethods.SendMessage(GEHWnd, (uint)NativeMethods.WM_COMMAND, NativeMethods.WM_PAINT, 0); 7: NativeMethods.PostMessage(GEHWnd, NativeMethods.WM_QT_PAINT, 0, 0); 8: 9: RECT mainRect = new RECT(); 10: NativeMethods.GetWindowRect(GEHWnd, out mainRect); 11: clientRect = new RECT(); 12: NativeMethods.GetClientRect(GEHrender, out clientRect); 13: 14: int offsetW = mainRect.Width - clientRect.Width; 15: int offsetH = mainRect.Height - clientRect.Height; 16: 17: int newWidth = this.Control.Width + (int)offsetW; 18: int newHeight = this.Control.Height + (int)offsetH; 19: 20: NativeMethods.SetWindowPos(GEHWnd, NativeMethods.HWND_TOP, 21: 0, 0, newWidth, newHeight, 22: NativeMethods.SWP_FRAMECHANGED); 23: 24: NativeMethods.SendMessage(GEHWnd, (uint)NativeMethods.WM_COMMAND, NativeMethods.WM_SIZE, 0); 25: }
此例子中对于鼠标消息到处理使用了钩子,调用HookAPI.dll实现。
1: ///2: /// 鼠标钩子 3: /// 4: private MouseHook mouseHook; 5: 6: // 设置鼠标钩子 7: mouseHook = new MouseHook(); 8: mouseHook.MouseClick += new MouseEventHandler(mouseHook_MouseClick); 9: mouseHook.MouseDbClick += new MouseEventHandler(mouseHook_MouseDbClick); 10: mouseHook.MouseWheel += new MouseEventHandler(mouseHook_MouseWheel); 11: // 启动鼠标钩子 12: mouseHook.StartHook(HookType.WH_MOUSE_LL, 0);
1: ///2: /// 鼠标钩子。鼠标单击事件 3: /// 4: /// 5: /// 6: void mouseHook_MouseClick(object sender, MouseEventArgs e) 7: { 8: IntPtr hWnd = NativeMethods.WindowFromPoint(e.Location); 9: if (hWnd == this.GeRenderHWnd) 10: { 11: Point point = this.Control.PointToClient(e.Location); 12: // 如果鼠标击点位置在控件内部,则说明鼠标点击了GoogleEarth视图 13: if (this.Control.ClientRectangle.Contains(point)) 14: { 15: Console.WriteLine("点击了GoogleEarth..."); 16: 17: DoublePoint dp = ((GERenderPanel)Control).DetermineScreenCoordinates(point.X, point.Y); 18: 19: ParameterizedThreadStart pts = new ParameterizedThreadStart(ShowMouseClickPoint); 20: 21: Thread thread = new Thread(pts); 22: thread.Start(dp); 23: 24: } 25: } 26: } 27: 28: protected void ShowMouseClickPoint(object obj) 29: { 30: //Thread.Sleep(20); 31: DoublePoint dp = (DoublePoint)obj; 32: PointOnTerrainGE pGe = GeApp.GetPointOnTerrainFromScreenCoords(dp.X, dp.Y); 33: Console.WriteLine("鼠标点击了:Lnt=" + pGe.Longitude.ToString() 34: + ";Lat=" + pGe.Latitude.ToString()); 35: }
双击事件:
1: ///2: /// 鼠标钩子。鼠标双击事件 3: /// 4: /// 5: /// 6: void mouseHook_MouseDbClick(object sender, MouseEventArgs e) 7: { 8: IntPtr hWnd = NativeMethods.WindowFromPoint(e.Location); 9: if (hWnd == this.GeRenderHWnd) 10: { 11: Point point = this.Control.PointToClient(e.Location); 12: // 如果鼠标击点位置在控件内部,则说明鼠标点击了GoogleEarth视图 13: if (this.Control.ClientRectangle.Contains(point)) 14: { 15: Console.WriteLine("xx双击了GoogleEarth..."); 16: 17: DoublePoint dp = ((GERenderPanel)Control).DetermineScreenCoordinates(point.X, point.Y); 18: 19: ParameterizedThreadStart pts = new ParameterizedThreadStart(ShowMouseDbClickPoint); 20: 21: Thread thread = new Thread(pts); 22: thread.Start(dp); 23: 24: } 25: } 26: } 27: 28: protected void ShowMouseDbClickPoint(object obj) 29: { 30: //Thread.Sleep(20); 31: DoublePoint dp = (DoublePoint)obj; 32: PointOnTerrainGE pGe = GeApp.GetPointOnTerrainFromScreenCoords(dp.X, dp.Y); 33: Console.WriteLine("xx鼠标双击了:Lnt=" + pGe.Longitude.ToString() 34: + ";Lat=" + pGe.Latitude.ToString()); 35: 36: MessageBox.Show("我还是出来一下吧!省得你不知道你已经双击了鼠标!"); 37: }
3、截图
程序中有两种截图功能,一种是GoogleEarth自带的截图功能,只能截取黑白图片;另一种为彩色截图,但是Vista以上操作系统不支持,还未找到合适的方法实现Vista与Win7兼容。
1) GoogleEarth自带截图功能:
1: GEViewContent view = GetGEView(); 2: 3: if (view != null) 4: { 5: ApplicationGE ge = view.GeApplication; 6: if (ge != null && ge.IsInitialized() > 0) 7: { 8: using (SaveFileDialog sfd = new SaveFileDialog()) 9: { 10: sfd.Filter = "jpg图片|*.jpg"; 11: sfd.AddExtension = true; 12: sfd.CheckPathExists = true; 13: sfd.Title = "保存Google Earth截图"; 14: 15: if (sfd.ShowDialog() == DialogResult.OK) 16: { 17: ge.SaveScreenShot(sfd.FileName, 100); 18: } 19: } 20: } 21: }
2) 彩色截图:
1: GEViewContent view = GetGEView(); 2: if (view != null) 3: { 4: int nWidth = view.Control.Width; 5: int nHeight = view.Control.Height; 6: Point pt = view.Control.PointToScreen(view.Control.Location); 7: int nXSrc = pt.X; 8: int nYSrc = pt.Y; 9: 10: IntPtr hRender = view.GeRenderHWnd; 11: 12: if (hRender != IntPtr.Zero) 13: { 14: // 取得Render DC 15: IntPtr hRenderDC = NativeMethods.GetWindowDC(hRender); 16: // 创建hBitmap 17: IntPtr hBitmap = NativeMethods.CreateCompatibleBitmap(hRenderDC, nWidth, nHeight); 18: // 创建MEM DC 19: IntPtr hMemDC = NativeMethods.CreateCompatibleDC(hRenderDC); 20: // 将Bitmap Select到MemDC 21: NativeMethods.SelectObject(hMemDC, hBitmap); 22: // 直接拷屏 23: NativeMethods.BitBlt(hMemDC, 0, 0, nWidth, nHeight, 24: hRenderDC, 0, 0, 13369376); 25: 26: using(Bitmap bmp = Bitmap.FromHbitmap(hBitmap)) 27: { 28: using(SaveFileDialog sfd = new SaveFileDialog()) 29: { 30: sfd.Filter = "JPG图片|*.jpg|PNG图片|*.png"; 31: sfd.AddExtension = true; 32: sfd.CheckPathExists = true; 33: sfd.Title = "保存Google Earth截图"; 34: 35: if (sfd.ShowDialog() == DialogResult.OK) 36: { 37: ImageFormat imgFormat = null; 38: // 默认选择JPG 39: if (sfd.FilterIndex == 0) 40: { 41: imgFormat = ImageFormat.Jpeg; 42: } 43: // 选择PNG 44: else 45: { 46: imgFormat = ImageFormat.Png; 47: } 48: bmp.Save(sfd.FileName, imgFormat); 49: } 50: } 51: 52: //销毁资源 53: NativeMethods.DeleteDC(hRenderDC); 54: NativeMethods.DeleteDC(hMemDC); 55: } 56: }
1)增加鼠标滚轮支持,可以实现放大、缩小。此功能利用上一篇提供的HookAPI.dll实现
2: // 放大 3: private const long ZoomIn = 0x00780000; 4: // 缩小 5: private const long ZoomOut = 0xFF880000; 6: ... 7: mouseHook.MouseWheel += new MouseEventHandler(mouseHook_MouseWheel); 8: ... 9: ///10: /// 鼠标钩子。鼠标滚动事件 11: /// 12: /// 13: /// 14: void mouseHook_MouseWheel(object sender, MouseEventArgs e) 15: { 16: IntPtr hWnd = NativeMethods.WindowFromPoint(e.Location); 17: if (hWnd == this.GeRenderHWnd) 18: { 19: Point point = this.Control.PointToClient(e.Location); 20: // 如果鼠标位置在控件内部,则说明鼠标在GoogleEarth视图范围内进行了滚动 21: if (this.Control.ClientRectangle.Contains(point)) 22: { 23: NativeMethods.PostMessage(GeRenderHWnd, (int)WM_MOUSE.WM_MOUSEWHEEL, e.Delta == 120 ? ZoomIn : ZoomOut, 0); 24: } 25: } 26: }
2)读取PlaceMarks(Google Earth界面中的位置)并显示、隐藏
3)读取所有图层,显示并隐藏,读取PlaceMarks
Google Earth COM API中提供了两个读取PlaceMarks的函数。一个是GetTemporaryPlaces(),用来读取临时位置;另一个是GetMyPlaces(),用来读取自定义位置,即GoogleEarth中显示的“我的位置”。呵呵
1: ... 2: ///3: /// 显示位置“PlaceMarks”。位置分为两种,一种时TemporaryPlaces,另一种为MyPlaces 4: /// 5: protected void ShowPlaces() 6: { 7: Thread.Sleep(500); 8: // 获取MyPlaces 9: FeatureGE myPlace = GeApp.GetMyPlaces(); 10: // 获取临时位置 11: FeatureGE temporaryPlace = GeApp.GetTemporaryPlaces(); 12: // 13: Listplaces = new List (); 14: places.Add(myPlace); 15: places.Add(temporaryPlace); 16: 17: // 获取工具面板 18: GEToolPad toolPad = GetToolPad(); 19: // 显示所有位置 20: toolPad.ShowPlaces(places); 21: } 22: ... 23: /// 24: /// 显示所有PlaceMarks(位置) 25: /// 26: /// 27: public void ShowPlaces(Listplaces) 28: { 29: if (this.InvokeRequired) 30: { 31: Action method = delegate { 32: InternalShowPlaces(places); 33: }; 34: 35: try 36: { 37: this.Invoke(method); 38: } 39: catch { } 40: } 41: else 42: { 43: InternalShowPlaces(places); 44: } 45: } 46: /// 47: /// 显示所有PlaceMarks(位置)。被ShowPlaces函数调用 48: /// 49: /// 50: protected void InternalShowPlaces(Listplaces) 51: { 52: this.tvPlaces.Nodes.Clear(); 53: 54: if (places == null || places.Count <= 0) 55: return; 56: 57: foreach (FeatureGE place in places) 58: { 59: TreeNode node = new TreeNode(place.Name); 60: node.Checked = place.Visibility > 0; 61: node.Tag = place; 62: 63: ShowChildrenPlaces(place, node); 64: 65: node.Expand(); 66: 67: this.tvPlaces.Nodes.Add(node); 68: } 69: } 70: /// 71: /// 显示指定PlaceMark的子PlaceMark 72: /// 73: /// 父PlaceMark 74: /// 父节点 75: protected void ShowChildrenPlaces(FeatureGE place, TreeNode node) 76: { 77: FeatureCollectionGE places = place.GetChildren(); 78: 79: foreach (FeatureGE child in places) 80: { 81: TreeNode tn = new TreeNode(child.Name); 82: tn.Checked = child.Visibility > 0; 83: tn.Tag = child; 84: 85: ShowChildrenPlaces(child, tn); 86: 87: node.Nodes.Add(tn); 88: } 89: }
3、读取图层
2: ///3: /// 显示图层 4: /// 5: protected void ShowLayers() 6: { 7: Thread.Sleep(500); 8: // 获取所有图层信息 9: FeatureCollectionGE layers = GeApp.GetLayersDatabases(); 10: // 获取工具面板 11: GEToolPad toolPad = GetToolPad(); 12: // 显示所有图层 13: toolPad.ShowLayers(layers); 14: } 15: ... 16: ///17: /// 将给定的图层集合显示在“图层”选项卡的树形控件中 18: /// 19: /// 图层集合 20: public void ShowLayers(FeatureCollectionGE layers) 21: { 22: if (this.InvokeRequired) 23: { 24: Action method = delegate { 25: InternalShowLayers(layers); 26: }; 27: try 28: { 29: this.Invoke(method); 30: } 31: catch { } 32: } 33: else 34: { 35: InternalShowLayers(layers); 36: } 37: } 38: ///39: /// 将给定的图层集合显示在“图层”选项卡的树形控件中.被公共函数ShowLayers调用 40: /// 41: /// 42: protected void InternalShowLayers(FeatureCollectionGE layers) 43: { 44: this.tvLayers.Nodes.Clear(); 45: 46: if (layers == null || layers.Count <= 0) 47: { 48: return; 49: } 50: 51: foreach (FeatureGE layer in layers) 52: { 53: TreeNode node = new TreeNode(layer.Name); 54: node.Checked = layer.Visibility > 0; 55: node.Tag = layer; 56: 57: ShowChidrenLayers(layer, node); 58: 59: node.Expand(); 60: 61: this.tvLayers.Nodes.Add(node); 62: } 63: } 64: ///65: /// 显示指定图层的子图层 66: /// 67: /// 当前图层 68: /// 当前节点 69: protected void ShowChidrenLayers(FeatureGE layer, TreeNode node) 70: { 71: FeatureCollectionGE layers = layer.GetChildren(); 72: 73: foreach (FeatureGE child in layers) 74: { 75: TreeNode tn = new TreeNode(child.Name); 76: tn.Checked = child.Visibility > 0; 77: tn.Tag = child; 78: 79: ShowChidrenLayers(child, tn); 80: 81: node.Nodes.Add(tn); 82: } 83: }