5.5 例子
本章通过两个与Google Maps + Ajax类似的宿营例子,展示一下Google Earth开发的乐趣所在,第一个是Delphi开发的例子,重点在与KML联动,第二个是C#开发的例子,重点在于设置地图视场相机的视角,实现虚拟飞行。与Google Maps API不同的是,Google Earth并没有提供有关添加点(Point)、线(Line)、面(Polygon)的接口,而是通过其他途径,如KML、自带的创建工具、第三方工具创建的。
第一个例子通过读取数据库中的二维关系表,列出所有参加野营活动的组别信息,每个组别的信息包括宿营地的经纬度、组别名称、人员组成等简单信息。将组别信息编写成KML脚本,并在Google Earth中执行,执行后的效果如图5-61所示。
如图5-62所示的是第三营队的宿营地在Google Earth上的截图。
图5-61 野营例子主界面 图5-62 Google Earth上的截图
第二个例子通过数据库中保存的野营各营队经纬度信息来调整地图视场相机的每一步的视角位置,从第一个宿营点自动“飞行”到最后一个宿营点。这个例子稍加更改,就是一个很有用的应用,例子的界面如图5-63所示,图5-64是运行后的效果。
图5-63 例子主界面 图5-64 Google Earth上的截图
另外在源代码中还有关于第二个例子“飞行”效果的演示视频。
5.5.1 开发环境配置
第一个例子采用Delphi语言+Google Earth的典型开发方案,数据库连接采用了Windows平台的优秀连接方式ADO。如表5-25所示的是开发环境配置简表。
表5-25 第一个例子开发配置
数据库 |
Access 2003 |
数据库连接方式 |
ADO |
开发平台 |
Delphi 7.0 |
Google Earth |
|
KML |
2.1版本 |
说明 |
Delphi的ADO开发,请读者参考其他相关资料,在此不再详细描述。 |
第二个例子采用Visual Studio 2005的C# + Google Earth联合开发,数据库采用SQLserver 2000,采用ADO.net的方式连接,如表5-26所示是开发环境配置简表。
表5-26 第二个例子开发配置
数据库 |
SQLServer |
数据库实例:getest |
|
用户名:sa 密码:123 |
|
数据库连接方式 |
ADO.net |
开发平台 |
Visual Studio 2005 C# |
Google Earth |
|
KML |
2.2版本 |
5.5.2 数据库设置
两个例子的数据库结构完全一致,第一个例子在名为“DATA.mdb”的Aceess数据库中建立一张名为“cantonment”的表,表的构建可以通过手工,也可以通过SQL语言建立,第二个例子在SQLServer中建立起一个名为“getest”的数据库。
数据表共有包括主键在内的5个字段:id、经度lng、纬度lat、组别org、人员信息备注remark。
建表的SQL语句如下:
CREATE TABLE [cantonment] (
[id] [int] NOT NULL ,
[lng] [float] NULL ,
[lat] [float] NULL ,
[org] [nvarchar] (8) COLLATE Chinese_PRC_CI_AS NULL ,
[remark] [nvarchar] (50) COLLATE Chinese_PRC_CI_AS NULL
) ON [PRIMARY]
GO
表中的内容如图5-65所示。
图5-65 数据表内容
DATA.mdb数据库放置在程序的根目录中,本例会自动找到它。
5.5.3 代码分析
本例只有一个窗体,其上设置了一个组别选择框,列出了所有参与这次野营活动的小组名称。根据组别名称,开启ADO连接,选择出与组别相符合的数据,列在DBGrid中。
另外还有两个按钮,一个是生成KML的按钮,其作用是根据列出在Grid中的数据动态生成KML脚本,并将脚本显示在文本框中;另一个按钮为【绘制】按钮,其作用是根据KML脚本调用Google Earth,执行KML并在地图上有所显示。
在程序窗口开始的时候,首先创建一个Google Earth进程,这是一些辅助程序,如读取程序根目录、执行一个ADOQuery等,如下:
unit Unit1;
……
var
fromMian: TfromMian;
tmpLng,tmpLat :string;
implementation
uses EARTHLib_TLB;
var F_AppGE: IApplicationGE;
{$R *.dfm}
……
function TfromMian.AppPath: widestring;
begin
result := extractfilepath(application.ExeName);
end;
//-----------------------------------------------------
function TfromMian.QryStr(var Qry: Tadoquery; SQLstr: string): boolean;
begin
try
with Qry do
begin
close;
sql.clear;
sql.add(SQLstr);
active := true;
end;
result := true;
except
result := false;
raise;
end;
end;
窗口启动,在窗口启动的同时启动Google Earth,并且配置ADO Connection,建立连接:
procedure TfromMian.FormCreate(Sender: TObject);
var
constr :string;
pathstr : widestring;
begin
try
F_AppGE := CoApplicationGE.Create();
except
showmessage('启动Google Earth失败');
end;
pathstr := AppPath;
constr := 'Provider=Microsoft.Jet.OLEDB.4.0;Data Source='+ pathstr +'DATA.mdb;Persist Security Info=False';
con.ConnectionString := constr;
con.Connected := true;
end;
选择框内容变更,打开ADOQuery执行一个相应的SQL查询,返回与选择组别对应的宿营地记录,代码如下:
procedure TfromMian.cmbChange(Sender: TObject);
begin
QryStr(qry,'select * from cantonment where org=''' + cmb.Text + '''');
QryStr(QryEXE ,'select avg(lng) as avglng, avg(lat) as avglat from cantonment where org ='''+ cmb.Text + '''');
with QryEXE do
begin
First;
//取经纬度平均值,便于观看全局
tmpLng := fieldbyname('avglng').AsString;
tmpLat := fieldbyname('avglat').AsString;
Close;
end;
end;
单击【生成KML语句】按钮,代码如下:
procedure TfromMian.btnKMLClick(Sender: TObject);
begin
//在memo中显示KML脚本
mm.Text := CreateKML;
end;
单击【绘制】按钮,其实质是调用OpendKmlFile方法,最终Google Earth会在执行后展示KML内容:
procedure TfromMian.btnDrawClick(Sender: TObject);
var
kmlstr:string;
begin
kmlstr := CreateKML;
SaveKMLFile(kmlstr);
F_AppGE.OpenKmlFile(AppPath + 'test.kml', 1);
end;
生成KML语句函数,代码如下:
function TfromMian.CreateKML: string;
var
kmlstr:string;
i:integer;
begin
kmlstr := '';
kmlstr := kmlstr + #13#10 + '
kmlstr := kmlstr + #13#10 + '
//循环取值,拼接KML语句
qry.First;
for i := 0 to qry.RecordCount -1 do
with qry do
begin
kmlstr := kmlstr + #13#10 + '
kmlstr := kmlstr + #13#10 + '
kmlstr := kmlstr + #13#10 + '
kmlstr := kmlstr + #13#10 + '
kmlstr := kmlstr + #13#10 + '
kmlstr := kmlstr + #13#10 + '
kmlstr := kmlstr + #13#10 + '
kmlstr := kmlstr + #13#10 + '
kmlstr := kmlstr + #13#10 + '
kmlstr := kmlstr + #13#10 + ' ';
kmlstr := kmlstr + #13#10 + '
kmlstr := kmlstr + #13#10 + '
kmlstr := kmlstr + #13#10 + ' ';
kmlstr := kmlstr + #13#10 + ' ';
qry.Next;
end;
kmlstr := kmlstr + #13#10 + ' ';
kmlstr := kmlstr + #13#10 + '';
result := kmlstr;
end;
保存KML至文件,文件名为“test.kml”,可以在程序的根目录下发现这个KML文件。读者也可以自己用Google Earth打开test.kml文件,效果一样:
procedure TfromMian.SaveKMLFile(kmldata:string);
var
FileName:string;
OutFile:TextFile;
begin
FileName := AppPath + 'test.kml';
AssignFile(OutFile,Filename);
reset(OutFile);
try
{if FileExists(Filename) then
begin
Append(OutFile);
Writeln(OutFile);
end
else }
Rewrite(OutFile);
Writeln(OutFile,kmldata);
finally
CloseFile(OutFile);
end;
end;
end.
运行程序后,单击【绘制】按钮,在Google Earth的界面上会出现加载的几个tesk.kml,以及每个KML文档中包含的地标子项,如图5-66所示。
图5-66 地标结构
这个动态生成的KML文档内容如下:
下面是3个点,分别用
如图5-67所示的是第一营队宿营地在Google Earth上的截图。
图5-67 Google Earth截图
这个例子可以结合IApplicationGE接口,稍加改动便可以成为自己程序的一部分。
第二个例子中的KML部分与第一个例子相似,在此不再赘述,重点在于“自动飞行”方面的代码讲解。
Google Earth新版中添加了一些激动人心的小应用,例如飞行模拟器,用户可以在【工具】菜单中选择【输入飞行模拟器】来调出飞行界面,然后按动方向键来控制飞机的上下左右飞行,获得类似驾驶飞机的身临其境的感受,结果这个小游戏一经推出大受欢迎。
其实这个小游戏的编写不难实现,最主要的是要掌握对地图视场相机的控制,Google Earth是假设有个相机对着“地面”的,用户可以通过鼠标或者键盘来控制相机视角来观察地图的,所以如果要想使得Google Earth的地图按照某种规律“自动”被浏览的话,就需要不断设置和调整视场相机的参数。
Google Earth API提供了一种方法SetCameraParams,该方法一共包含8个参数,分别是lat 纬度,lon 经度,alt 高度,altMode 高度模式,Range 范围,tilt 倾角,Azimuth 方位角,speed 速度,具体的讲解请参见表5-3。本例通过在timer中按照每个间隔调整一次参数,来实现飞行的目的,下面讲解代码。
首先需要设置一些全局变量:
bool isfly = false; //是否已经开始“飞行”
int recordpace = 0; //飞行的步骤间距
double lat1; //飞行在两点间,前一个点的纬度
double lat2; //飞行在两点间,后一个点的纬度
double lng1; //飞行在两点间,前一个点的经度
double lng2; //飞行在两点间,后一个点的经度
double tmp_lat = 0; //每步“飞行”的纬度(不断累加)
double tmp_lng = 0; //每步“飞行”的经度(不断累加)
int i = 0; //飞行计数
在例子程序窗口打开的时候,需要创建GE对象实例,并且连接数据库,执行SQL语句,将数据表中的数据取出,并且调用生成KML的函数段,生成KML最终将KML文本内容显示在程序窗体上:
private void Form1_Load(object sender, EventArgs e)
{
try
{
appGE = new ApplicationGEClass();
ConnDB tmpDb = new ConnDB("server=127.0.0.1;database=getest;uid=sa;pwd=123;");
dbset = tmpDb.ReturnDataSet("select * from cantonment");
dbview.DataSource = dbset.Tables[0];
txtKML.Text = makeKML(dbset);
lat1 = (double)dbset.Tables[0].Rows[recordpace]["lat"];
lat2 = (double)dbset.Tables[0].Rows[recordpace + 1]["lat"];
lng1 = (double)dbset.Tables[0].Rows[recordpace]["lng"];
lng2 = (double)dbset.Tables[0].Rows[recordpace + 1]["lng"];
tmp_lat = lat1;
tmp_lng = lng1;
}
catch (Exception err)
{
MessageBox.Show("未能启动Google Earth程序");
}
}
上面的代码涉及到了ADO.net的操作:
new ConnDB("server=127.0.0.1;database=getest;uid=sa;pwd=123;");
这个代码在程序方案的db项目中有相关的源代码,请自行参阅相关代码和文档。下面是生成KML文本的函数与Delphi例子中的类似,请参见Delphi的例子,下面是生成KML文档后在键盘上建立ge.kml文件的代码:
private void CreateKMLFile(string textContent, string filepath)
{
try
{
FileStream kmlfile = File.Create(filepath);
byte[] strdata = new UTF8Encoding().GetBytes(textContent);
kmlfile.Write(strdata, 0, strdata.Length);
kmlfile.Flush();
kmlfile.Close();
}
catch (Exception err)
{
throw (err);
}
}
而生成KML之后,程序代码中还赋值了“飞行”中第一个点和第二个点的经纬度信息,目的是为了以后的“飞行”每一步经纬度的计算做准备,在程序中设置为:将每两点间的距离分成50步来走,每一步的飞行间隔为100毫秒(0.1秒),这一切是通过一个Timer对象来完成的,如图5-68所示。
图5-68 Timer的属性设置
下面的Timer执行代码中体现了50步走的设置,以及每一步要走多远的计算:
private void timerFly_Tick(object sender, EventArgs e)
{
if (isfly == true)
{
i = i + 1;
tmp_lat = tmp_lat - calcLatDelta(lat1, lat2);
tmp_lng = tmp_lng - calcLngDelta(lng1, lng2);
SetCameraView(tmp_lat, tmp_lng, 200, 20, 80, 4.9);
if (i == 50) //分为50步走
{
recordpace = recordpace + 1;
if (recordpace < dbset.Tables[0].Rows.Count-1)
{
lat1 = (double)dbset.Tables[0].Rows[recordpace]["lat"];
lat2 = (double)dbset.Tables[0].Rows[recordpace + 1]["lat"];
lng1 = (double)dbset.Tables[0].Rows[recordpace]["lng"];
lng2 = (double)dbset.Tables[0].Rows[recordpace + 1]["lng"];
i = 0;
tmp_lat = lat1;
tmp_lng = lng1;
}
}
}
}
代码中的calcLatDelta为计算每一步走多远的函数,通过将前后两个点的经纬度相减后的差除以50后得到,同样这里也能反映出分为50步走的思路:
private double calcLngDelta(double lng1, double lng2)
{
return (lng1 - lng2) / 50;
}
本例设置为100毫秒走一步,共分50步走完一个点,是出于用户体验的考虑,在实际应用中可根据需要自行调节快慢。
最后,在【飞行】按钮中,设置timerFly.Start();开启Timer对象,实现每100毫秒触发一次的“飞行”,执行后,在地图窗口会自动平滑地从宿营点1“浏览飞行”到宿营点6,就好像有一个无形的鼠标在控制着Google Earth一样。