一、动态标记 现在,既然我们已经对Google Maps API工作机理有了一个基本的理解,并且了解了Ajax对我们有什么帮助;那么接下来,让我们来探讨如何使用Ajax技术把一幅Google地图集成到我 们的Java web应用程序中。作为示例,在我们的Elbonian网站上,我们想显示Elbonia地区所有的旅游景点。你可能想实现一种交互式地图;从中,用户能 够使用地图上的标记定位一个特定的旅游景点。当用户点击一个景点时,一个显示有详细信息的窗口弹出。
为了实现以上目的,首先要集成一组服务器提供的标记。这样做的一种方法是在服务器端动态地生成JavaScript代码。然而,这是相当麻烦的,并 且会导致生成过度庞大的HTML页面。幸好,还存在一种更好的方法。Google Maps API提供了一个称为GDownloadUrl()的方便的函数,你可以通过这个函数以XML形式下载数据并且在你的JavaScript代码中处理它。 这样以来,把你的XML数据转换成Google Map标记是很容易的。
这个Ajax应用程序在其服务器端利用了仅有的一个servlet。这个servlet以XML形式提供关于旅游景点和包含这些旅游景点城市的数 据。这个servlet的实现细节对于这里的讨论来说并不特别重要;最重要的是,这个servlet基于它接收的查询参数返回的是XML(而不是 HTML)数据:
public class TouristSiteDirectoryServlet
extends javax.servlet.http.HttpServlet
implements javax.servlet.Servlet {
...
/**
*数据存取对象提供了搜索旅游景点数据库的方法
*/
private SiteDAO getSiteDAO() {
...
}
/**
*提供的数据存取对象提供了搜索旅游景点数据库的方法
*/
protected void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
String method=request.getParameter("method");
XStream xstream = new XStream();
xstream.alias("marker", Marker.class);
response.setContentType("application/xml");
if (method == null) {
String id = request.getParameter("id");
Site site = getSiteDAO().findSiteById(id);
xstream.alias("site", Site.class);
String xml = xstream.toXML(site);
response.getWriter().write(xml);
} else if (method.equals("findAll")) {
List
sites = getSiteDAO().findAllAsMarkers();
String xml = xstream.toXML(sites);
response.getWriter().write(xml);
} else if (method.equals("findAllCities")) {
List
cities = getSiteDAO().findAllCityAsMarkers();
String xml = xstream.toXML(cities);
response.getWriter().write(xml);
} else if (method.equals("findByCity")) {
String city = request.getParameter("city");
List
sites = getSiteDAO().findByCityAsMarkers(city);
String xml = xstream.toXML(sites);
response.getWriter().write(xml);
}
}
}
|
这里大多数的代码也大都加入了注释。大致说来,这个servlet经由DAO对象实现数据库查询,并使用XStream技术把数据转换成XML形式 (还可以把这些数据存储在一个普通XML文件中)。当然,把地理站点坐标存储在一个数据库中的优点是可以根据需要对它们进行更新—例如,在发生一场大型地 震或陆地滑坡或大陆漂移等情况下。
然后,这个XML被直接写到servlet响应流中。结果列表看起来如下所示:
<list>
<marker>
<id>3</id>
<name>The Famous Mud Baths of central Elbonia</name>
<latitude>-36.87</latitude>
<longitude>185.22</longitude>
</marker>
<marker>
<id>5</id>
<name>The North Elbonian Mud Baths</name>
<latitude>-36.77</latitude>
<longitude>185.92</longitude>
</marker>
...
</list> |
在客户端,我们取回XML标记列表,并且在地图上生成一组相应的标记。为了实现这一目的,我们经由一个Google工具类—GXmlHttp—来使 用Ajax。这个类把一个查询发送到服务器端以取回一个XML文档—然后你可以使用JavaScript DOM函数对之加以分析。
下面,让我们来分析一下如何在真实环境下应用上面的技术。上面的XML数据由loadSites()函数(它再调用processSiteData()函数来处理站点数据)取回:
<script language="javascript" type="text/javascript">
function loadSites() {
var request = GXmlHttp.create();
request.open("GET", "/maps/SiteDirectory?method=findAll", true);
request.onreadystatechange = getCallbackFunction(request, processSiteData);
request.send(null);
}
</script> |
这个processSiteData()函数及相关联的displaySiteMarkers()函数,都使用了JavaScript DOM处理技术来提取关于旅游景点的有用数据,并且在地图上创建相应的标记:
<script language="javascript" type="text/javascript">
function processSiteData(xmlDoc){
//取得标记数组并循环遍历它
siteMarkers = xmlDoc.documentElement.getElementsByTagName("marker");
displaySitesMarkers();
}
function displaySitesMarkers() {
map.clearOverlays();
for (var i = 0; i < siteMarkers.length; i++) {
//获得每个标记的属性
var lat = parseFloat(siteMarkers[i].getElementsByTagName("latitude") [0].firstChild.nodeValue);
var lng = parseFloat(siteMarkers[i].getElementsByTagName("longitude") [0].firstChild.nodeValue);
var id = siteMarkers[i].getElementsByTagName( "id")[0].firstChild.nodeValue;
var label = siteMarkers[i].getElementsByTagName( "name")[0].firstChild.nodeValue;
createSiteMarker(new GLatLng(lat,lng),label,id);
}
}
</script>
|
二、实时显示细节信息
当用户点击一个景点时,我们想弹出一个信息框来显示该景点的细节信息(参考图3)。这在一个基于地图的应用程序中是经常要求实现的。
图3.显示细节信息
然而,在真正世界应用程序中,经常存在太多的数据需要立即全部加载。你需要取回并且根据特定要求实现相应的显示。为此,我们将再次使用Ajax技术。首先,我们在标记中添加一个监听器—调用openInfoWindow()函数实现:
<script language="javascript" type="text/javascript">
GEvent.addListener(marker, "click", function() {
openInfoWindow(marker,"" + id);
});
</script> |
这个openInfoWindow()函数显示一个临时消息(“Loading details...”),然后继续从服务器取回详细的信息。这种临时消息对用户来说是一种重要的可视化线索,从而让用户知道请求正在处理中。
<script language="javascript" type="text/javascript">
function openInfoWindow(marker, id) {
marker.openInfoWindowHtml("Loading details...");
fetchDetails(id,marker);
}
</script> |
该fetchDetails()函数很类似于前面我们所看到的Ajax代码(displayDetails()函数),它也使用了JavaScript DOM来提取一些有用的域,然后准备HTML细节文本以显示在信息窗内:
<script language="javascript" type="text/javascript">
function fetchDetails(id) {
var req = GXmlHttp.create();
req.open("GET", "/maps/SiteDirectory?id="+id, true);
req.onreadystatechange = getCallbackFunction(req, displayDetails);
req.send(null);
}
function displayDetails(siteDetailsXML) {
//从文档中取得根“site”元素
var site = siteDetailsXML.getElementsByTagName("site")[0];
var name = getNodeValue(site.getElementsByTagName("name")[0]);
var id = getNodeValue(site.getElementsByTagName("id")[0]);
var symbol = getNodeValue(siteDetailsXML.getElementsByTagName( "symbol")[0]);
var website = getNodeValue(siteDetailsXML.getElementsByTagName( "website")[0]);
var address = site.getElementsByTagName("address")[0]
var address1 = getNodeValue(siteDetailsXML.getElementsByTagName( "line1")[0]);
var address2 = getNodeValue(siteDetailsXML.getElementsByTagName( "line2")[0]);
var city = getNodeValue(siteDetailsXML.getElementsByTagName( "city")[0]);
var postcode = getNodeValue(siteDetailsXML.getElementsByTagName( "postcode")[0]);
marker = getMarker(id);
marker.showMapBlowup();
var html = '<span class="site-title-line">'
+ name + ' (' + symbol + ')'
+'</span>'
+ '<span class="site-details-line">'
+ address1
+'</span>'
+ '<span class="site-details-line">'
+ address2
+'</span>'
+ '<span class="site-details-line">'
+ city + ' ' + postcode
+'</span>'
+ '<span class="site-details-line">'
+ '<a href="' + website + '">' + website + '</a>'
+'</span>'
marker.openInfoWindowHtml(html);
} </script>
|
三、总结
显然还存在许多更高级的话题没有在本文中涉及。例如,在这种类型的实际应用程序中,可能存在相当多的位置(或旅游景点,或待售的房子)需要轻松地显 示在一幅小规模的地图上。因此,一开始,当要显示整个国家时,我们仅需要标记出我们所感兴趣的城市,而当用户点击一个城市时,地图将放大并且显示在站点中 显示这个相应的城市。借助于在本文尚未讨论的其它Google Map API特征,这种功能是很容易实现的;但是,你可以在本文相应的示例代码中看到这样的例子。
最后,希望本文能够为你使用Google Maps和Ajax技术在你的web应用程序中集成基于地图的功能助一臂之力。作为一篇入门性文章,这里所涉及的服务器端Ajax技术是非常基本的。对于更复杂的应用程序来说,例如DWR这样的Ajax框架是非常值得深入探讨的。