[转载]基于Servlet的Google Earth之旅
从这一点来说Google Earth客户端是我们时代的技术标志。Google Earth并非是第一个地球浏览客户端,而且与它的先驱、不为人知的Keyhole非常相似。但是凭着Google的大名以及基础版对最终用户免费,它完 成了市场渗透并得到公认――这是另一个值得大书特书的有趣话题。
本文只有一个基本使命:即向你展示在servlet和Google Earth客户端之间发送和接收信息是多么的容易。有了这种程度的交互,你就能用基本的Java编程技能创建设想的服务。
使用许可及竞争者
截至本文发稿时Google Earth还处于beta阶段(版本号3.0.0616),许可证是商业的(见客户端的帮助部分)。如果你想寻求等价的开源范例,我建议你去关注优秀的Nasa World Wind(Nasa世界风)项目
基础知识
Google Earth客户端以第二版的锁位标记语言(KML)解析XML数据,它有一个专用的命名空间。庞大的KML配置信息可能会影响到GUI显示,开发这种需要平衡利弊的应用的难点在于需要了解更多的KML细节而不是编程技巧。KML实体的简要列表包括:
*Placements(位置),标明在地球上的坐标
*Folders(夹子),帮助组织其它的特征信息
*Documents(文档),存放可能包含风格元素的folder的容器
*Image overlays(图片叠加),用来添加图片
*Network links(网络链接),描述在何处以及如何与服务器或者servlet(本文采用的方式)连接
本文为了简化的目的,主要探讨了folder、placement和network-link元素的使用;此外还用folder定义了一段旅程(tour),它里面包含了一系列的placement。
在Windows上安装了Google Earth后,文件扩展名KML和MIME(Multipurpose Internet Mail Extensions,多用途网络邮件扩展)类型“application/keyhole”即被注册。这意味着只要点击KML文件或通过TCP/IP接 收“application/keyhole”MIME类型的文件就会激活Google Earth客户端。
如果返回的KML文本为:
Hello World [127.0.0.1]
则程序将显示如下内容:
图1 Hello World folder的GUI显示
要想激活Earth客户端,只需浏览适当的URL地址--就好比从资源地址(http://localhost:8080/Tour/hello)下载HelloServlet源程序。这样就能激活doGet()方法,然后重定向到doPost()方法,在所有的Web浏览器里都会看到以下结果:
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 ="Hello World ["
+ request.getRemoteAddr()+ "]";
out.println(message);
}
不要小看这段简单的代码,里面的方法暗藏着玄机。服务器可以作为各种数据类型和Google Earth之间的中介。不妨设想像这样一个场景:在旅程数据中包含有不同的XML方言,在返回响应前由服务器完成扩展风格语言(Extensible Stylesheet Language)的转换。再进一步,服务器可以选择返回哪一种响应,以允许个性化处理。KML文档实体允许风格定义,可根据IP地址范围改变风格,使得 不同的用户看到的风格可能会不一样。
作为实践,我们将从使用Google Earth和输出KML文件开始。在Google Earth的顶部是Add菜单,可以在这里添加placement、folder和image overlay,然后用File菜单保存生成的KML文件。我强烈推荐编辑导出的XML文件以了解改动对Google Earth的影响。好了,让我们开始与这位世界之王共舞!
了解城市定位
本节给出一个面向教学的应用:一个用来教授学生城市名称与地理位置间关系的程序。我们将创建一个以类似于抽签的方式将城市位置随机发送给客户端的 servlet。城市的位置(placement)用KML表示。Placement实体里封装了HTML链接,将用户引导到相关的有趣站点。这样我们就 可以使用户在Web浏览器和Google Earth间进行交互。
学生可以通过在鼠标置于链接之上时出现的菜单中选择Refresh来选择下一个placement,如图2所示。
图2 刷新网络链接生成一个新位置(在这里是伦敦)时的GUI显示
我们这个应用的后台处理用到了network-link(网络链接)实体,network-link从http://location加载数据文件。将此文件存于桌面并双击,Google Earth开始运行,并从服务器端加载下面的KML代码段。
City.kml
<?xml version="1.0" encoding="UTF-8"?>
Refresh me
Random City
1
1
1
1
http://location
该配置中的实体含义为:
*visibility(可见性),定义了网络链接是否可见
*open(展开),说明是否展开标签
*refreshVisibility(刷新可见性),定义是否取代用户对刷新位置可见性的设定
*flyToView(巡视),如果设为1,用户可以在View窗口“飞越”位置上空
许多实体通常都可以跨根元素使用(如description)。注意标签名是大小写敏感的,所以编码时要小心以免出现难以排查的错误。在我看来,各标签值与它们对GUI的交互作用关系并不总是符合逻辑的,因此你可能对任何新的KML代码段的运用都需要花些时间。
注意
在默认情况下,Firefox、Opera和IE浏览器对于从Web上接收的扩展名为kml的文件反应是不同的。激活网络链接文件最通用的方法是避免服 务器将KML文件初始化,并允许用户将文件下载到桌面,这样就能通过双击来启动它们。另一种更好的方法是将KML文件嵌入到JSP(JavaServer Pages)页面里并允许JSP页面返回“application/keyhole”MIME类型的KML代码段。假使对内容类型做修改并去掉XML模 式,city.jsp就成了city.kml文件。
该代码的开头为:
< NetworkLink> 回到前面的代码,servlet返回了一个在description元素中带有HTML代码的placement。为 遵守XML规范,我们将HTML代码段放入分割标签中,以避免使XML解析器混淆:
London
London]]>
London, UKroot://styleMaps#default+nicon=0x304+hicon=0x314
-0.1261969953775406,51.50019836425783,50
在placement里出现了三个新实体:
*address(地址),包含地址的逻辑标签
*styleUrl,定义在此处要显示的图片
*Point/coordinates(点/坐标),位置的柱面坐标
Servlet通过以下代码生成一个随机的placement响应:
manager.KMLRenderOfRandomPlacement();
我们的整个应用都是最基础的,servlet没有保持跟踪状态。Management类根据数据的组织重画各个窗口。Manager.java的 init方法将数据加载到property bean数组中。显然,真实的应用需要与数据库通信,象iBATIS或Hibernate这样的持久层管理框架将会很有用。placement bean用来为返回的placement准备数据,该bean有一个代表其自身的属性点。当开发者对KML编程的细节以及如何到达Google Earth GUI中的某个点有了更多的了解之后,就可以对此模型进行扩充。
下面的QuizServlet是对Manager.java的轻量封装,该servlet对每个post或get请求都返回一个有效的KML响应。
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={
"London",
"Rome",
"University of Amsterdam"};
this.idx=coords.length;
cityArray= new PlacementBean[coords.length];
//Init the array of placements
for (int i =0; 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=" "+
" "+pBean.getName()+" "+
" "+
"
"+
" "+pBean.getStyleURL()+" "+
" "+
" "+pBean.getCoordinates().getCoordinate()+" "+
" "+
" ";
return klmString;
}
}
为了直接将远程服务器上的图片加到placement上,styleUrl标签需要一个指向Web的链接(如http:/imageServer/image.gif),这就使代码能在View窗口的placement处填充一个图片(在本应用中是一个国旗)。
对此方法做进一步研究,就可以设计出一个场景:用户在与Google Earth客户端交互的同时还能填写Web表单。图3给出了这一基本构思的示意图。
做进一步研究,就可以设计出一个场景:用户在与Google Earth客户端交互的同时还能填写Web表单。图3给出了这一基本构思的示意图。
在两个servlet服务器的前端是Apache Web服务器。第一个是表单服务器,根据发送的参数返回Web表单;第二个是旅程服务器,生成placement列表封装在folder中成为一个旅程。 旅程服务器处理图片的URL,图片本身以静态方式存储于文件系统中以改善性能。 互动流程如下: 由此可见,基于上述构想创建一个具备功能性和教育性的应用是可能的。然而,我们还不能以定期的方式直接从客户端向servlet反馈信息,除非学生对位置进行刷新。在下一部分我们将深入探讨这一问题。 双向交流 在上面的代码示例中,网络链接需要等待我们的刷新操作。幸运的是,我们可以让Google Earth以get方法定期地发送View窗口中用户的位置,如下所示: Examples of bi directional flow of information Lets send coordinates once in a while http://localhost:8081/Tour/message 实际的动作由Url实体完成。viewRefreshTime标签定义了经过多少秒服务器接收下一套Earth坐标,viewRefreshMode标签设置为onStop就意味着当停止在View窗口里移动时更新Earth坐标。图4是上述配置最终效果的一个截图。
好了,我们可以把那些讨厌的坐标发给服务器了。我们可以用它来做什么呢?让我们从创建一个消息服务开始。图5给出了两个流程。
首先,通过浏览器发送消息并接收坐标: 返回生成的位置(placement)类似于下面的KML: Alan Berg]]> 4.889999,52.369998,0 以下就是构成这一协奏乐章的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"?> "
+ " "
+ " "
+ ""+name+"]]> "
+""+lastDate+"]]> "
+ " "
+ ""+userlon+","+userlat+",0 "
+ " "
+ " "
+ " ";
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="
Location: "+lastLocation.getCoords()+ " IP address: "+lastLocation.getIpAddress()+ " Updated: "+lastLocation.getUpdated(); } response.setContentType("text/html"); out = response.getWriter(); out.println(message); }} 来自浏览器的消息保存在静态成员LastMessageBean中,坐标保存在LastLocationBean中,且每个bean都只有一个实例。此 外,在执行getting或setting操作时对所有的静态bean都进行同步。我们用单个实例来达到简化的目的,有助于限制要编写的代码数量。然而, 更有实用价值的示例应具备跟踪IP地址的会话管理功能并生成相应的处理结果。 有一个不起眼的错误,在placement实体的名字标签里使用HTML标签会导致显示问题:整个标签在Google Earth客户端的“places”菜单区按HTML显示,但在View窗口里却按文本显示。我认为这种不一致是个bug。 在本示例中Google Earth客户端推送坐标,servlet返回KML代码段。既然知道能用坐标推送上下文关联信息,我们可以强制通过段中的链接来进行交互,必要的话还可 以让浏览器成为宿主。本文展示了如何控制Google Earth客户端,至此你已拥有了一个创建自己互动旅程的概念性工具箱。
来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/374079/viewspace-131040/,如需转载,请注明出处,否则将追究法律责任。
转载于:http://blog.itpub.net/374079/viewspace-131040/
[转载]基于Servlet的Google Earth之旅相关推荐
- 【GEE python】基于geemap开发Google earth App
文章目录 前言 一.GEE python学习的"磨刀石" 二.geemap及其基本用法 1.geemap介绍 2. 比较常用的类 3.geemap的基本操作 三.地图圈取,获取经纬 ...
- 基于GEE(Google earth engine)的 GIMMS NDVI月合成
这里写自定义目录标题 动机 代码 动机 项目需要用到长时间序列的NDVI数据,一般人常用到的是MODIS的月合成产品,而忽略了GIMMS,是因为其空间分辨率低:但是其依然具有其他产品无法比拟的优势,那 ...
- 【收藏】基于QGIS的Google Earth Engine插件已发布
https://zhuanlan.zhihu.com/p/98060635?utm_source=qq
- Google Earth 背后的故事
Google Earth 可以说是最知名和最广泛使用的地理信息系统 (GIS) 软件.它能够创建.存储.查看与位置相关的数据并与之交互.凭借其多用途设计和广泛的工具集,该软件针对跨越教育.政府.房地产 ...
- Google Earth 成长历程的15个小故事
利用 Google Earth,可以像宇航员一样从太空中看到我们的星球,只需点击或轻触几秒钟就可以在地球上的任何地方旅行.如今的 Google Earth 仍然是世界上最大的可公开获取的地理图像存储库 ...
- 基于google earth engine(GEE)下载研究区域影像
基于google earth engine(GEE)下载研究区域影像 当研究需要Landsat数据时,我们可以通过USGS官网或者地理空间数据云平台下载.由于地理空间数据云目前无法下载到较新的数据,可 ...
- 基于Google Earth Engine的Landsat单窗算法地表温度(LST)反演
基于Google Earth Engine的Landsat单窗算法地表温度(LST)反演 1 背景知识 2 算法介绍 3 代码 4 效果 1 背景知识 基于遥感数据的地表温度(LST)反演目前得到 ...
- 基于google earth 高程数据三维地形建模
最近看了看三维图形图像方面的一些资料,以及一些三维地形建模的文章.做了一个三维地形建模的程序.三维地形的应用范围比较广,可以做地质测量,仿真,军事上可以建立三维地形模型,做一些军事演习仿真之类的. 程 ...
- 五个基于Google Earth的小游戏
五个基于Google Earth的小游戏 Google Earth是互联网巨头Google公司于2005年推出的一款虚拟地球仪软件,它把卫星照片.航空照相和地理信息系统布置在一个地球的三维模型上,使用 ...
- 基于Google Earth Engine Explorer谷歌地球引擎GEE浏览界面实现遥感影像地物监督分类
本文主要对GEE的网页界面式应用程序接口Google Earth Engine Explorer加以详细介绍与地物监督分类这一具体应用.本文是谷歌地球引擎(Google Earth Engine, ...
最新文章
- R语言使用pwr包的pwr.f2.test函数对线性回归模型(Linear models)进行效用分析(power analysis)的语法
- latex 甘特图_[简004] 史上'最全最精简'Markdown语法-速览速用
- NeurIPS 2021 | 图上不均衡表示学习新视野:基于拓扑结构的不均衡学习
- boost::mpl::advance相关用法的测试程序
- python 函数递归一次增加一次变量_python基础之函数、返回值,局部变量、全局变量,递归(继续补充不定长参数)...
- linux编码 form表单,Linux curl 模拟form表单提交信息和文件
- python基础之“换行符”的应用
- scrapy 伪装_Python scrapy 伪装代理和fake_userAgent的使用
- php+mysql多事务处理_php+mysql事务处理
- asp怎么循环增加字段和字段对应的值_索引该怎么创建?
- LINUX获得毫秒时间戳的代码
- 储存程序是现代计算机的,A、信息论B、存储程序原理C、现代计算机理论D.DOC
- Sqlmap命令大全
- 在 After Effects 中最受欢迎的10大AE插件推荐
- javaweb项目大概轮廓
- mysql导入(ibd文件)
- vim菜鸟学习-杂篇(windows篇)
- sdkman 的酷炫 Logo 欢迎界面: 安装 kscript (Kotlin Shell)
- OPA277/OPA2277/OPA4277 High Precision Operational Amplifiers 高精度运放
- html5怎么查看路由器状态,怎么看路由器是否正常_怎么看路由器是否联网?-192路由网...
热门文章
- 在 Vue 中使用lodash对事件进行防抖和节流
- 北航计算机专业最低分,北京航空航天大学2020年本科录取线发布,最低分572
- 【BZOJ4414】数量积
- GPU 编程与CG 语言之阳春白雪下里巴人——CG学习读书笔记之数学函数(三)
- C#导出VCF格式电话本,遇到QUOTED-PRINTABLE编码的问题,附带QUOTED-PRINTABLE编码解码
- 不积跬步无以至千里---工作收获
- (零基础)如何使用python下载哔哩哔哩视频?
- AURIX TC397 Multicore 多核
- leetcode 803.打砖块(C/C++/Java/python)
- 带色彩恢复的多尺度视网膜增强算法(MSRCR)的原理、实现及应用