颜庭柏,姚 迪,沈 澄
(1.南京市气象局 信息网络中心,江苏 南京 200009;2.南京市六合气象局 气象台,江苏 南京 200009)
保险、铁路、电力、航运、建筑、工矿、商场等行业对气象的要求和依赖越来越高。提供天气预报和专业气象信息服务的前提是要提供快速、可靠、直观的天气实况信息。虽然气象专业软件MICAPS可以显示基准气象站的天气实况,但对于由各地方自行建设的自动气象站和雨量站的显示需要开发软件来实现。本文基于Google Map JavaScript API 第3版开发了天气实况显示的Google Map服务, 实现了添加地图控件、事件响应与处理、本地搜索等常用功能,使用户可以对区域内温度、雨量等气象数据一目了然。
Google Map是Google公司开发的一款虚拟地图软件,它有覆盖全球的航天和卫星图片,其卫星影像图解析度高、更新快,最为普通用户关注的是它提供的服务是免费的。相应的Google Map API是Google公司提供的一套用来操作Google Map的地图应用接口函数库,它有多个版本。本系统使用的Google Map JavaScript API 第3版由JavaScript脚本实现,提供了基于Google Map的多种地图模式(交通、卫星影像、地形等)的各种层面的调用和扩展接口。利用这些接口函数库,用户可以快速地建立自己的网站地理信息系统。Google Maps API 提供的最重要的类是Map, 它代表页面上的地图对象,用户可以根据需要在页面上使用多个 Map 的实例[1]。API开发包提供给用户的动态函数库让开发者可以为每个实例提供一些指定的事件, 并利用静态方法GEventaddListener或GEvent.bind监视这些事件。此外API还提供GMarker、GIcon、GPolyline、GLargeMapControl等类来分别实现标注、标记、折线、控件等功能[2]。
Ajax是Asynchronous JavaScript 和XML 的简称,也就是异步的JavaScript 和XML 处理技术[3]。Ajax是一个客户端技术,通过调用HttpRequst 实现与服务器的异步通信,并最终在网页中实现丰富友好的用户界面。因此,Ajax 并不是一种技术,而是已经成功地用于现代浏览器中的若干成功技术的组合。这些技术包 括:XHTML、CSS、DOM、XML和XSLT以 及JavaScript等。传统的基于Web的应用程序,每次向服务器发出请求后,该页面将全部刷新以显示新内容;基于Ajax 异步方法的因特网服务,客户端Web页面可以发送多个服务器请求,请求发出后,服务器根据客户端发出的请求作出回应,只返回对客户请求所需的数据信息,从而实现页面的局部刷新而不是全部刷新[4]。
本系统是一个典型的基于Ajax技术的Google Maps应用模型,大致分为5个层次: 客户端浏览器、Ajax引擎、Google Maps服务器、Web服务器和数据库[6]。系统网络体系结构层次如下:
1)客户端浏览器。它的任务是访问Web 服务器中的相关页面,并请求地图数据,显示包含天气实况的地图信息。
2)Ajax引擎,处于用户与服务器之间。它的主要任务是实现页面无刷新的传递,解决网络交互过程中用户长时间的等待以及数据更新时屏幕闪烁的问题。
3)Google Maps 服务器。它在页面加载之后显示从Google Maps 服务器获得地图数据。
4)Web 服务器,接收到来自浏览器端的请求后,对请求进行处理、分析、计算等。如果需要数据库中的数据,则对数据库中的信息数据进行一系列操作处理,并把处理后的数据返回给客户端浏览器,连同地图信息一起呈现给用户。
5)数据库,用以存储天气实况信息的数据库。本系统使用的是SQL Server 2005。系统结构如图1所示。
图1 系统结构图
1)可对地图进行平移缩放、查看比例尺、切换不同类型地图以及缩略图等方便快捷的操作。还可通过定制控件来改变控件的外观等属性,从而实现个性化的地图界面。
2)在网站中根据需要设置了不同属性天气实况数据的分类查看功能, 把温度、湿度、气压等要素分布在不同图层以方便用户查询。
3)通过添加自定义的控件及控件事件,实现快速导航功能,在系统中可以通过导航按钮将显示的界面快速切换到不同的地理区域。
4)在提供地图显示功能的同时,提供数据列表显示、历史资料查询等功能。
本系统使用的是Google Maps JavaScript 应用程序接口,动态网页的脚本用JavaScript编写,在网页文件中使用标志包含JavaScript脚本。首先创建一个DIV元素来存放Google Map的地图,为了确保系统在完全载入网页后再显示,程序设计中在HTML网页的
元素收到onload事件后,执行initialize函数来初始化地图,构建 Map 实例对象。初始化工作中包括对地图中心经纬度、地图类型的确定以及添加地图缩放等控件。初始化要实现的功能有确定地图中心经纬度、地图放大倍数、地图中心位置和地图显示的类型等信息。var myLatlng = new google.maps.LatLng(32.0000, 118.7853);
var myOptions =
{ zoom: 12,
center: myLatlng,
mapTypeId: google.maps.MapTypeId.TERRAIN,
scrollWheel: false,
mapTypeControl: true
}
为了客户端在浏览网页时不重复加载地图,在网页的首部添加AjaxControlToolkit 动态链接库的引用。
ASP.NET中ScriptManager控件用于页面的客户端脚本资源,每一个使用ASP.NET技术的页面都需要添加ScriptManager控件,来标明该页面使用的是ASP.NET Ajax框架,ScriptManager控件的使用语法如下:
UpdatePanel控件是用来实现页面的无刷新技术的控件。开发时只要在UpdatePanel控件中包含需要刷新的区域,则每次浏览器向服务器端发出请求后,只有该面板内的部分才会被 刷新,以此代替了以往整个页面都需要回调来获取请求的方式。在本系统中,将列表部分放在Updatepanel面板内,在数据更新时,只有列表部分会自动刷新数据。
除了地图缩放、类型选择等API控件的样式和位置外,在Google Map中还可以创建自己的控件以处理与用户的交互操作。控件浮动在地图之上的绝对位置处,所以从本质上讲控件只是一个在地图上具有绝对位置的DIV元素,它会向用户显示某个用户界面,并处理与用户或地图的交互(通常通过某个事件处理程序)。在本系统中创建了几个定位控件,当点击事件发生时,按控件的要求将地图的中心位置定位到对应的经纬度,以此方便用户在不同地理区域间切换。创建的步骤是:
1)为要显示的控件元素定义适当 CSS。
2)针对地图属性更改用户事件,本系统中使用Click 事件,通过事件处理程序处理与用户的交互。
3)创建一个
var myDIV=document.createElement('DIV');
var myControl=new HomeControl(myDIV,map,eachSta);google.maps.event.addDomListener(controlUI, 'click',function (){ map.setCenter(latlng) });
天气实况资料每10 min更新一次,为了做到实时更新资料,系统以1 min间隔检查数据库是否更新。当发现数据更新时,将最新数据导入到DataSet数据集中,便于数据的显示。DataSet是不依赖于数据库的独立数据集合,即使断开数据链路或者关闭数据库,DataSet依然是可用的。
在读取数据后,以叠加层的方式将天气实况资料和地图结合起来,Google Maps API V3版提供了用于创建自定义叠加层的OverlayView类。OverlayView是一个基类,提供了在创建叠加层时必须实现的若干方法。本系统中,天气资料显示在一个自定义的Label中,再将Label叠加到地图上。
1)将自定义对象的 prototype 设置为google.maps.OverlayView()的新实例。这可以有效地实现叠加层类的“子类化”。
2)为自定义叠加层创建构造函数,并将该构造函数中的所有初始化参数都设置为自定义属性。
function MyMarker(latlng, map, labelText) {
this.latlng_ = latlng;
this.text_ = labelText;
this.setMap(map);
}
3)在原型中实现onAdd()方法,以将叠加层附加到地图上。当地图准备好附加叠加层后,系统将会调用OverlayView.onAdd()。
4)在原型中实现draw()方法,以处理对象的视觉显示。同样,在对象首次显示后,系统将会调用OverlayView.draw()。 另外还应当实现onRemove()方法,以清理在叠加层中添加的所有元素。以下为在Label中添加气象要素值,并将其叠加到地图中指定的经纬度的代码:
MyMarker.prototype.draw = function () {
var me = this;
var div = this.div_;
if (!div) {
div = this.div_ = document.createElement('DIV');
div.style.border = "none";
div.style.position = "absolute";
div.style.paddingLeft = "0px";
div.style.cursor = 'pointer';
div.innerHTML = "