国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 開發(fā) > 綜合 > 正文

基于Servlet的Google Earth之旅

2024-07-21 02:14:52
字體:
供稿:網(wǎng)友

  從這一點(diǎn)來說google earth客戶端是我們時代的技術(shù)標(biāo)志。google earth并非是第一個地球?yàn)g覽客戶端,而且與它的先驅(qū)、不為人知的keyhole非常相似。但是憑著google的大名以及基礎(chǔ)版對最終用戶免費(fèi),它完成了市場滲透并得到公認(rèn)――這是另一個值得大書特書的有趣話題。

  本文只有一個基本使命:即向你展示在servlet和google earth客戶端之間發(fā)送和接收信息是多么的容易。有了這種程度的交互,你就能用基本的java編程技能創(chuàng)建設(shè)想的服務(wù)。

使用許可及競爭者

  截至本文發(fā)稿時google earth還處于beta階段(版本號3.0.0616),許可證是商業(yè)的(見客戶端的幫助部分)。如果你想尋求等價的開源范例,我建議你去關(guān)注優(yōu)秀的nasa world wind(nasa世界風(fēng))項(xiàng)目

基礎(chǔ)知識

  google earth客戶端以第二版的鎖位標(biāo)記語言(kml)解析xml數(shù)據(jù),它有一個專用的命名空間。龐大的kml配置信息可能會影響到gui顯示,開發(fā)這種需要平衡利弊的應(yīng)用的難點(diǎn)在于需要了解更多的kml細(xì)節(jié)而不是編程技巧。kml實(shí)體的簡要列表包括:
*placements(位置),標(biāo)明在地球上的坐標(biāo)
*folders(夾子),幫助組織其它的特征信息
*documents(文檔),存放可能包含風(fēng)格元素的folder的容器
*image overlays(圖片疊加),用來添加圖片
*network links(網(wǎng)絡(luò)鏈接),描述在何處以及如何與服務(wù)器或者servlet(本文采用的方式)連接

  本文為了簡化的目的,主要探討了folder、placement和network-link元素的使用;此外還用folder定義了一段旅程(tour),它里面包含了一系列的placement。

  在windows上安裝了google earth后,文件擴(kuò)展名kml和mime(multipurpose internet mail extensions,多用途網(wǎng)絡(luò)郵件擴(kuò)展)類型“application/keyhole”即被注冊。這意味著只要點(diǎn)擊kml文件或通過tcp/ip接收“application/keyhole”mime類型的文件就會激活google earth客戶端。

  如果返回的kml文本為:
<folder><name>hello world [127.0.0.1] </name></folder>

  則程序?qū)@示如下內(nèi)容:


圖1 hello world folder的gui顯示

  要想激活earth客戶端,只需瀏覽適當(dāng)?shù)膗rl地址--就好比從資源地址(http://localhost:8080/tour/hello)下載helloservlet源程序。這樣就能激活doget()方法,然后重定向到dopost()方法,在所有的web瀏覽器里都會看到以下結(jié)果:
protected void doget(httpservletrequest request, httpservletresponse response)
   throws servletexception, ioexception
{
   dopost(request, response);
}

protected void dopost(httpservletrequest request, httpservletresponse response)
    throws servletexception, ioexception
{
   response.setcontenttype("application/keyhole");
   printwriter out = response.getwriter();  
   string message ="<folder><name>hello world ["
  + request.getremoteaddr()+ "]</name></folder>";
   out.println(message);
}
  不要小看這段簡單的代碼,里面的方法暗藏著玄機(jī)。服務(wù)器可以作為各種數(shù)據(jù)類型和google earth之間的中介。不妨設(shè)想像這樣一個場景:在旅程數(shù)據(jù)中包含有不同的xml方言,在返回響應(yīng)前由服務(wù)器完成擴(kuò)展風(fēng)格語言(extensible stylesheet language)的轉(zhuǎn)換。再進(jìn)一步,服務(wù)器可以選擇返回哪一種響應(yīng),以允許個性化處理。kml文檔實(shí)體允許風(fēng)格定義,可根據(jù)ip地址范圍改變風(fēng)格,使得不同的用戶看到的風(fēng)格可能會不一樣。

  作為實(shí)踐,我們將從使用google earth和輸出kml文件開始。在google earth的頂部是add菜單,可以在這里添加placement、folder和image overlay,然后用file菜單保存生成的kml文件。我強(qiáng)烈推薦編輯導(dǎo)出的xml文件以了解改動對google earth的影響。好了,讓我們開始與這位世界之王共舞!

了解城市定位

  本節(jié)給出一個面向教學(xué)的應(yīng)用:一個用來教授學(xué)生城市名稱與地理位置間關(guān)系的程序。我們將創(chuàng)建一個以類似于抽簽的方式將城市位置隨機(jī)發(fā)送給客戶端的servlet。城市的位置(placement)用kml表示。placement實(shí)體里封裝了html鏈接,將用戶引導(dǎo)到相關(guān)的有趣站點(diǎn)。這樣我們就可以使用戶在web瀏覽器和google earth間進(jìn)行交互。

  學(xué)生可以通過在鼠標(biāo)置于鏈接之上時出現(xiàn)的菜單中選擇refresh來選擇下一個placement,如圖2所示。


圖2 刷新網(wǎng)絡(luò)鏈接生成一個新位置(在這里是倫敦)時的gui顯示

  我們這個應(yīng)用的后臺處理用到了network-link(網(wǎng)絡(luò)鏈接)實(shí)體,network-link從http://location加載數(shù)據(jù)文件。將此文件存于桌面并雙擊,google earth開始運(yùn)行,并從服務(wù)器端加載下面的kml代碼段。
city.kml
<?xml version="1.0" encoding="utf-8"?>
<kml xmlns="http://earth.google.com/kml/2.0">
    <networklink>
      <description>refresh me</description>
      <name>random city</name>
      <visibility>1</visibility>
      <open>1</open>
      <refreshvisibility>1</refreshvisibility>
      <flytoview>1</flytoview>
      <url>
        <href>http://location </href>
      </url>
    </networklink>
</kml>

該配置中的實(shí)體含義為:
*visibility(可見性),定義了網(wǎng)絡(luò)鏈接是否可見
*open(展開),說明是否展開標(biāo)簽
*refreshvisibility(刷新可見性),定義是否取代用戶對刷新位置可見性的設(shè)定
*flytoview(巡視),如果設(shè)為1,用戶可以在view窗口“飛越”位置上空

  許多實(shí)體通常都可以跨根元素使用(如description)。注意標(biāo)簽名是大小寫敏感的,所以編碼時要小心以免出現(xiàn)難以排查的錯誤。在我看來,各標(biāo)簽值與它們對gui的交互作用關(guān)系并不總是符合邏輯的,因此你可能對任何新的kml代碼段的運(yùn)用都需要花些時間。

注意
  在默認(rèn)情況下,firefox、opera和ie瀏覽器對于從web上接收的擴(kuò)展名為kml的文件反應(yīng)是不同的。激活網(wǎng)絡(luò)鏈接文件最通用的方法是避免服務(wù)器將kml文件初始化,并允許用戶將文件下載到桌面,這樣就能通過雙擊來啟動它們。另一種更好的方法是將kml文件嵌入到j(luò)sp(javaserver pages)頁面里并允許jsp頁面返回“application/keyhole”mime類型的kml代碼段。假使對內(nèi)容類型做修改并去掉xml模式,city.jsp就成了city.kml文件。
該代碼的開頭為:
<%response.setcontenttype("application/keyhole");%>
    <networklink>  回到前面的代碼,servlet返回了一個在description元素中帶有html代碼的placement。為遵守xml規(guī)范,我們將html代碼段放入<!cdata[]]>分割標(biāo)簽中,以避免使xml解析器混淆:

<placemark>
   <name>london</name>
   <description>
<![cdata[<a >london</a>]]>
</description>
   <address>london, uk</address>
   <styleurl>root://stylemaps#default+nicon=0x304+hicon=0x314</styleurl>
   <point>
      <coordinates>-0.1261969953775406,51.50019836425783,50</coordinates>
   </point>
</placemark>

  在placement里出現(xiàn)了三個新實(shí)體:
*address(地址),包含地址的邏輯標(biāo)簽
*styleurl,定義在此處要顯示的圖片
*point/coordinates(點(diǎn)/坐標(biāo)),位置的柱面坐標(biāo)

  servlet通過以下代碼生成一個隨機(jī)的placement響應(yīng):
manager.kmlrenderofrandomplacement();
  我們的整個應(yīng)用都是最基礎(chǔ)的,servlet沒有保持跟蹤狀態(tài)。management類根據(jù)數(shù)據(jù)的組織重畫各個窗口。manager.java的init方法將數(shù)據(jù)加載到property bean數(shù)組中。顯然,真實(shí)的應(yīng)用需要與數(shù)據(jù)庫通信,象ibatis或hibernate這樣的持久層管理框架將會很有用。placement bean用來為返回的placement準(zhǔn)備數(shù)據(jù),該bean有一個代表其自身的屬性點(diǎn)。當(dāng)開發(fā)者對kml編程的細(xì)節(jié)以及如何到達(dá)google earth gui中的某個點(diǎn)有了更多的了解之后,就可以對此模型進(jìn)行擴(kuò)充。

  下面的quizservlet是對manager.java的輕量封裝,該servlet對每個post或get請求都返回一個有效的kml響應(yīng)。
quizservlet.java
package test.google.earth.servlet;

import java.io.ioexception;
import java.io.printwriter;
import javax.servlet.servletexception;
import javax.servlet.http.*;
import javax.servlet.servletconfig;
import test.google.earth.manager.manager;

public class quizservlet extends httpservlet
{
   private manager manager;

   public void init(servletconfig config) throws servletexception {
      super.init(config);
      this.manager= new manager();
      manager.init();
   }

   protected void doget(httpservletrequest request, httpservletresponse response)
   throws servletexception, ioexception
   {
   dopost(request, response);
   }

   protected void dopost(httpservletrequest request, httpservletresponse response)
   throws servletexception, ioexception
   {
   response.setcontenttype("application/keyhole");
   printwriter out = response.getwriter();
   out.println(manager.kmlrenderofrandomplacement());
   }
}
manager.java

package test.google.earth.manager;


import java.util.random;
import test.google.earth.bean.placementbean;
import test.google.earth.bean.pointbean;

public class manager {
   private placementbean[] cityarray;
   private string styleurl;
   private string open;
   private random generator;
   private int idx;

   public manager(){}

public void init(){
   this.styleurl="root://stylemaps#default+nicon=0x304+hicon=0x314";
   this.open="1";
   this.generator = new random();
   string[] coords = {"-0.1261969953775406,51.50019836425783,50",
   "12.5,41.889999,50","4.889999,52.369998,0"};
   string[] name = {"london","italy","amsterdam"};
   string[] address={"london, uk","rome, italy","amsterdam, netherlands"};
   string[] description={
   "<a href=/"http://www.visitlondon.com/choose_site/?originalurl=//">london</a>",
   "<a href=/"http://www.roma2000.it//">rome</a>",
   "<a href=/"http://www.uva.nl//">university of amsterdam</a>"};
   this.idx=coords.length;


   cityarray= new placementbean[coords.length];

//init the array of placements
   for (int i =0; i<coords.length;i++){
      placementbean placementbean = new placementbean();
      placementbean.setaddress(address[i]);
      placementbean.setdescription(description[i]);
      placementbean.setname(name[i]);
      placementbean.setopen(open);
      placementbean.setstyleurl(styleurl);
      pointbean pointbean = new pointbean();
      pointbean.setcoordinate(coords[i]);

      placementbean.setcoordinates(pointbean);
      this.cityarray[i]=placementbean;
   }
}

public synchronized placementbean nextrandomplacement(){
   return cityarray[ generator.nextint( this.idx )];
}

public synchronized string kmlrenderofrandomplacement(){

   return renderkmlplacement(nextrandomplacement());
}

private string renderkmlplacement(placementbean pbean){
   string klmstring="<placemark>/n"+

   "/t<name>"+pbean.getname()+"</name>/n"+
   "/t<description><![cdata["+pbean.getdescription()+"]]></description>"+
   "/t<address>"+pbean.getaddress()+"</address>/n"+
   "/t<styleurl>"+pbean.getstyleurl()+"</styleurl>/n"+
   "/t<point>/n"+

   "/t/t<coordinates>"+pbean.getcoordinates().getcoordinate()+"</coordinates>/n"+
   "/t</point>/n"+
   "</placemark>/n";
   return klmstring;
   }
}

  為了直接將遠(yuǎn)程服務(wù)器上的圖片加到placement上,styleurl標(biāo)簽需要一個指向web的鏈接(如http:/imageserver/image.gif),這就使代碼能在view窗口的placement處填充一個圖片(在本應(yīng)用中是一個國旗)。
  對此方法做進(jìn)一步研究,就可以設(shè)計(jì)出一個場景:用戶在與google earth客戶端交互的同時還能填寫web表單。圖3給出了這一基本構(gòu)思的示意圖。

做進(jìn)一步研究,就可以設(shè)計(jì)出一個場景:用戶在與google earth客戶端交互的同時還能填寫web表單。圖3給出了這一基本構(gòu)思的示意圖。


圖3 基于表單的旅行服務(wù)的潛在基本構(gòu)思

  在兩個servlet服務(wù)器的前端是apache web服務(wù)器。第一個是表單服務(wù)器,根據(jù)發(fā)送的參數(shù)返回web表單;第二個是旅程服務(wù)器,生成placement列表封裝在folder中成為一個旅程。旅程服務(wù)器處理圖片的url,圖片本身以靜態(tài)方式存儲于文件系統(tǒng)中以改善性能。

  互動流程如下:
1.        用戶登錄到表單服務(wù)器。
2.        服務(wù)器通過目錄服務(wù)(可以是輕量目錄訪問服務(wù))驗(yàn)證用戶身份,并將用戶的ip地址存入一個會話表中。
3.        表單服務(wù)器重定向到旅程服務(wù)器。
4.        旅程服務(wù)器檢查正在會話中的已注冊用戶的ip地址。
5.        根據(jù)存儲在數(shù)據(jù)庫中的用戶歷史信息返回一個旅程。
6.        google earth聚焦到一個位置(placement)并請求一張圖片。
7.        用戶點(diǎn)擊placement中的一個鏈接,觸發(fā)表單服務(wù)器生成并返回一個表單。
8.        學(xué)生填寫表單,然后繼續(xù)旅行。
9.        如此幾番后,學(xué)生退出會話,引發(fā)應(yīng)用向相關(guān)教師發(fā)送一個將學(xué)生的回答轉(zhuǎn)化為專用格式報告的email,至此服務(wù)器完成了作業(yè)的交付。

  由此可見,基于上述構(gòu)想創(chuàng)建一個具備功能性和教育性的應(yīng)用是可能的。然而,我們還不能以定期的方式直接從客戶端向servlet反饋信息,除非學(xué)生對位置進(jìn)行刷新。在下一部分我們將深入探討這一問題。

雙向交流

  在上面的代碼示例中,網(wǎng)絡(luò)鏈接需要等待我們的刷新操作。幸運(yùn)的是,我們可以讓google earth以get方法定期地發(fā)送view窗口中用戶的位置,如下所示:
<?xml version="1.0" encoding="utf-8"?>
<kml xmlns="http://earth.google.com/kml/2.0">
<folder>
   <description>examples of bi directional flow of information</description>
   <name>network links</name>
   <visibility>1</visibility>
   <open>1</open>
   <networklink>
      <description>lets send coordinates once in a while</description>
      <name>message pushing</name>
      <visibility>1</visibility>
      <open>1</open>
      <refreshvisibility>1</refreshvisibility>
      <flytoview>0</flytoview>
      <url>
         <href>http://localhost:8081/tour/message</href>
         <refreshinverval>2</refreshinverval>
         <viewrefreshmode>onstop</viewrefreshmode>
         <viewrefreshtime>1</viewrefreshtime>
      </url>
    </networklink>
</folder>
</kml>
  實(shí)際的動作由url實(shí)體完成。viewrefreshtime標(biāo)簽定義了經(jīng)過多少秒服務(wù)器接收下一套earth坐標(biāo),viewrefreshmode標(biāo)簽設(shè)置為onstop就意味著當(dāng)停止在view窗口里移動時更新earth坐標(biāo)。圖4是上述配置最終效果的一個截圖。


圖4 網(wǎng)絡(luò)鏈接和關(guān)聯(lián)html的gui顯示

  好了,我們可以把那些討厭的坐標(biāo)發(fā)給服務(wù)器了。我們可以用它來做什么呢?讓我們從創(chuàng)建一個消息服務(wù)開始。圖5給出了兩個流程。


圖5 google earth、servlet和瀏覽器之間的信息流

  首先,通過瀏覽器發(fā)送消息并接收坐標(biāo):
1.        瀏覽器以post方法發(fā)送參數(shù)名和消息
2.        serlet以類似于以下形式的文本消息返回從google earth客戶端收到的最后坐標(biāo):
location: -0.134539,51.497,-0.117855,51.5034
ip address: 127.0.0.1
updated: fri oct 21 11:42:45 cest 2005
  其次,在servlet與google earth客戶端之間傳遞坐標(biāo)并接收位置(placement):
1.        每經(jīng)過δt時間,google earth通過get方法發(fā)送用戶在view窗口中的坐標(biāo)。
2.        servlet把消息放在placement中返回,placement通過坐標(biāo)來粗略計(jì)算在何處放置返回的消息。請注意我已從kml教程中將對中算法拷貝過來。

  返回生成的位置(placement)類似于下面的kml:
<?xml version="1.0" encoding="utf-8"?>
<kml xmlns="http://earth.google.com/kml/2.0">
   <placemark>
      <name><![cdata[<font color="red">alan berg</font>]]></name>/
      <description><![cdata[blah blah <i> fri oct 21 11:42:45 cest 2005</i>]]>
      </description>
      <point>
         <coordinates>4.889999,52.369998,0</coordinates>
      </point>
   </placemark>
</kml>

 以下就是構(gòu)成這一協(xié)奏樂章的servlet代碼:

messageservlet.java
package test.google.earth.servlet;

import java.io.ioexception;
import java.io.printwriter;
import javax.servlet.servletconfig;
import javax.servlet.servletexception;
import javax.servlet.http.*;

import test.google.earth.bean.lastlocationbean;
import test.google.earth.bean.lastmessagebean;

import java.util.date;

public class messageservlet extends httpservlet
{
   private static lastmessagebean lastmessage=new lastmessagebean();
   private static lastlocationbean lastlocation= new lastlocationbean();

   public void init(servletconfig config) throws servletexception {
   super.init(config);
   lastmessage.setmessage("no message yet");
   lastmessage.setname("system");
   lastmessage.setupdated(new date());
   lastlocation.setcoords("no contact with a client yet");
   lastlocation.setipaddress("");
   lastlocation.setupdated(new date());
}

   protected void doget(httpservletrequest request, httpservletresponse response)
   throws servletexception, ioexception
{
   string coords = request.getparameter("bbox");
   if (coords==null){
   return;
   }

   string message;
   string name;
   date lastdate;
   string ipaddress = request.getremoteaddr();

   synchronized(this) {
      lastlocation.setcoords(coords);
      lastlocation.setipaddress(ipaddress);
      lastlocation.setupdated(new date());
      message=lastmessage.getmessage();
      name=lastmessage.getname();
      lastdate=lastmessage.getupdated();
   }

   response.setcontenttype("application/keyhole");
   printwriter out = response.getwriter();
   string[] coparts= coords.split(",");
   float userlon;
   float userlat;
   try{
      userlon = ((float.parsefloat(coparts[2]) - float.parsefloat(coparts[0]))/2)+
         float.parsefloat(coparts[0]);
      userlat = ((float.parsefloat(coparts[3]) - float.parsefloat(coparts[1]))/2) +
         float.parsefloat(coparts[1]);
   }catch(numberformatexception e){
      return;
   }

      string klmstring = "<?xml version=/"1.0/" encoding=/"utf-8/"?>/n"
      + "<kml xmlns=/"http://earth.google.com/kml/2.0/">/n"
      + "<placemark>/n"
      + "<name><![cdata[<font color=/"red/">"+name+"</font>]]></name>/n"

      +"<description><![cdata["+message+"<br><i>"+lastdate+"</i>]]></description>/n"
      + "<point>/n"
      + "<coordinates>"+userlon+","+userlat+",0</coordinates>/n"
      + "</point>/n"
      + "</placemark>/n"
      + "</kml>/n";      
      out.println(klmstring);
}

   protected void dopost(httpservletrequest request, httpservletresponse response)
   throws servletexception, ioexception
{
   string name = request.getparameter("name");
   if (name==null){
      return;
   }
   string message;
   printwriter out;

   synchronized(this) {
   lastmessage.setmessage(request.getparameter("message"));
   lastmessage.setname(name);
   lastmessage.setupdated(new date());
   message="<pre>/nlocation: "+lastlocation.getcoords()+
   "/nip address: "+lastlocation.getipaddress()+
   "/nupdated: "+lastlocation.getupdated();
   }


   response.setcontenttype("text/html");
   out = response.getwriter();
   out.println(message);
   }
}  
  來自瀏覽器的消息保存在靜態(tài)成員lastmessagebean中,坐標(biāo)保存在lastlocationbean中,且每個bean都只有一個實(shí)例。此外,在執(zhí)行g(shù)etting或setting操作時對所有的靜態(tài)bean都進(jìn)行同步。我們用單個實(shí)例來達(dá)到簡化的目的,有助于限制要編寫的代碼數(shù)量。然而,更有實(shí)用價值的示例應(yīng)具備跟蹤ip地址的會話管理功能并生成相應(yīng)的處理結(jié)果。

  有一個不起眼的錯誤,在placement實(shí)體的名字標(biāo)簽里使用html標(biāo)簽會導(dǎo)致顯示問題:整個標(biāo)簽在google earth客戶端的“places”菜單區(qū)按html顯示,但在view窗口里卻按文本顯示。我認(rèn)為這種不一致是個bug。

  在本示例中g(shù)oogle earth客戶端推送坐標(biāo),servlet返回kml代碼段。既然知道能用坐標(biāo)推送上下文關(guān)聯(lián)信息,我們可以強(qiáng)制通過段中的鏈接來進(jìn)行交互,必要的話還可以讓瀏覽器成為宿主。本文展示了如何控制google earth客戶端,至此你已擁有了一個創(chuàng)建自己互動旅程的概念性工具箱。

發(fā)表評論 共有條評論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 绥滨县| 滦南县| 衡东县| 原阳县| 松潘县| 酉阳| 沛县| 静海县| 宣城市| 太和县| 金寨县| 万安县| 大新县| 含山县| 江达县| 叙永县| 合阳县| 广汉市| 新巴尔虎右旗| 秀山| 留坝县| 揭东县| 齐河县| 临沭县| 九江县| 思南县| 永善县| 农安县| 高唐县| 浪卡子县| 蛟河市| 彰化县| 密山市| 容城县| 望奎县| 武城县| 彭水| 班戈县| 梁平县| 方山县| 乌鲁木齐市|