一种基于Android的移动互联网GIS设计与实现
2014-02-19安晓飞李治洪李虹霖
安晓飞,李治洪,李虹霖
(1. 华东师范大学 资源与环境科学学院 地理信息科学教育部重点实验室,上海 200241)
随着移动互联网的发展,面向移动客户端的GIS应用已成为一种趋势。Google、百度、高德等互联网地图服务商迅速推出了基于移动应用的地图API。专业的GIS软件公司如ESRI也推出了移动GIS的开发包。目前,利用Google地图等API开发的移动地图应用主要利用其已有的数据和功能,开发面向公众的服务。由于数据和功能上的原因,不适用于开发面向专业领域的移动GIS应用。本文讨论一种面向专业领域、基于Android的移动GIS技术框架和关键技术。
1 技术框架
本文讨论的移动GIS技术框架分为服务器端和客户端两部分。服务器端使用自主研发的Web地图服务器(GeoServer),通过HTTP协议提供地图浏览、查询分析、专题制图等功能接口。该地图服务器采用了对象池技术,当客户端发出一个地图请求时,GeoServer从对象池中调用一个空闲的地图引擎实例(MapServer),来完成地图的查询分析任务。MapServer实例的数量和启动,可根据服务器资源自动调整,因此响应速度非常快[1,2](图1)。
客户端主要包括2个部分:地图视图(MapView)与通讯模块。MapView是一个可重用的可视化容器组件,负责地图显示和处理与用户的交互,如地图缩放和查询操作。MapView继承自ViewGroup类,封装了ImageView和ZoomControls两个子View,用户只需将MapView加入到已有的布局(Layout),并调用相应类的成员方法[3],就可以开发相关的地图应用。
通讯模块主要负责向GeoServer地图服务器发送数据请求,并处理服务器返回的请求结果。用户在MapView上的每一次操作,都会通过后台线程(Background Thread)向服务器发出数据请求,当收到服务器返回的数据后,后台线程向主线程(UI Thread)发送消息(Message),由MapView负责地图内容的更新,从而实现地图缩放、平移等效果。客户端及服务器端的整体框架如图1。
图1 移动客户端GIS框架图
2 关键技术
2.1 地图服务器通信协议和接口
目前,移动互联网应用中,移动客户端和服务器端的通信方式主要有基于Socket和HTTP协议2种方式[4]。由于地图的请求和交互操作不需要时刻保持监听,因此我们选择HTTP通信方式。
GeoServer针对常用的地图操作请求,设计并提供了相应的数据访问接口,客户端只需要按照相应的请求格式,使用HTTP协议,以GET/POST方式向服务器发送请求,并处理返回的响应结果即可[5]。常用的请求接口如表1。
表中xxx为主机名,8080为端口号。Webmap接口用来提供地图浏览服务;querymap接口提供地图的查询服务;参数map是所请求地图的名称;zoom表示地图的缩放范围,通过改变zoom的值实现地图的放大和缩小;maptool表示当前的地图操作模式,如平移、缩放、查询地图等;参数return表示返回数据的格式(如json)。
表1 GeoServer常用的地图操作请求接口
由于HTTP是一种无状态协议,每次发出的请求都被视作一个独立的连接。同时,由于性能上的考虑,我们采用共享地图服务实例的方式,所以进行查询或连续缩放地图等操作时,需要有一个机制来保存用户的地图状态。我们是通过Cookie技术来实现的。当地图客户端在第一次连接服务器时,从头文件中获取一个SessionID,示例代码如下:
String cookieValue=httpcon.getHeaderField("Set-Cookie");
sessionID=cookieValue.substring(0, cookieValue.indexOf(";"));
在后续的每次请求中,我们再将SessionID以Cookie的方式返回给地图服务器:
httpcon.setRequestProperty("Cookie", sessionID);
这样就能保证在初始化一个地图实例后,sessionID作为服务器端和客户端的令牌(token)和关键字,辅助服务器来唯一标识各客户端地图的状态。客户端和服务器端HTTP通信的具体流程如图2所示。
图2 实现地图功能的HTTP通信流程
2.2 移动客户端多线程应用框架
当地图客户端第一次启动时,Android会启动一个相应的主线程,负责处理与UI相关的事件,如用户的按键事件、触摸屏幕的事件以及屏幕绘图等。主线程通常又叫作UI线程。
在移动客户端地图应用中,有许多非常耗时的任务,如网络访问、复杂计算、图片下载等。为了避免UI线程的阻塞,提高交互的流畅性,通常以新建并开启后台线程的方式,异步处理容易导致阻塞的任务[6]。
后台线程主要负责请求地图数据并通知UI线程来更新地图内容。我们设计并使用Handler对象作为信使(Courier),负责消息的发送、消息内容的处理等工作,后台线程就是通过传进来的Handler对象发送消息。主线程收到消息后进行地图更新操作。代码框架如下:
private class BackgroundThread extends Thread {
@Override
public void run() {
switch(operateType){
…………//根据地图操作类型处理与Geoserver服务器的通信
}
//处理完成后使用Handler发送消息
Message msg = new Message();
msg.what = COMPLETED;
handler.sendMessage(msg);
}
}
UI线程收到后台通信模块发过来的消息后,就处理消息,即更新地图。主要代码如下:
private Handler handler=new Handler(){
@Override
public void handleMessage(Message msg) {
if(msg.what==COMPLETED){
if(myBitmap==null){
Toast.makeText(mContext, "没有获得地图数据,请检查网络
连接或重新启动尝试", Toast.LENGTH_LONG).show();
}else{
//接收到数据,更新地图
mapContainer.setImageBitmap(myBitmap);
}
}
super.handleMessage(msg);
}
};
2.3 Android触摸操作及手势识别
在移动设备上操作地图,必须支持触摸和手势。与地图相关的触摸和手势主要有:双击实现地图的放大、两点触控缩放地图以及通过滑动来平移地图等。
2.3.1 两点触控缩放地图
Android应用开发框架已经封装好了相应的触摸事件,用户在不同的View控件上触摸产生的Touch消息将被TouchListener获取并进行处理。我们可以在地图容器控件中设置setOnTouchListener处理器进行监听,然后在onTouch()方法中处理相应操作,从而实现地图的缩放、平移等。代码框架如下:
mapContainer.setOnTouchListener(new OnTouch Listener(){
@Override
public boolean onTouch(View v, MotionEvent event) {
mapContainer=(ImageView)v;
switch(event.getAction()& MotionEvent.ACTION_MASK){
case MotionEvent.ACTION_DOWN:
//记录第一次touch的位置坐标
break;
case MotionEvent.ACTION_POINTER_DOWN:
eventDistance=calcDistance(event);
break;
case MotionEvent.ACTION_MOVE:
if(touchState==ZOOM){
float dist=calcDistance(event);
if(dist>eventDistance){
operateType=MAPZOOMIN;//当两点距离增大时放大地图
}else{
operateType=MAPZOOMOUT;//当两点距离缩小时缩小地图
}
new WorkThread().start();
}
break;
}
return true;
}
});
在onTouch方法中,当监听到MotionEvent.ACTION_DOWN时,通过event参数获取触摸的两个点的坐标,并计算出初始的距离。当MotionEvent.ACTION_MOVE时,即当两指移动时,再计算出当前两点距离,如果距离变大,则放大地图,反之则缩小地图。
2.3.2 手指滑动平移地图
当系统监听一个滑动事件时,参数e1、e2分别为滑动的初始位置和终点位置,velocityX和velocityY分别代表在x、y两个方向上的滑动速率。通过计算和变换,MapView把初始位置和终点位置的坐标传给后台线程,然后由后台线程向服务器发送请求。示例代码如下:
public abstract boolean onFling (MotionEvent e1,MotionEvent e2,
float velocityX, float velocityY){
if(e1!=null&&e2!=null){
x1=(int)e1.getX();
y1=(int)e1.getY();
x2=(int)e2.getX();
y2=(int)e2.getY();
operateType=MAPFLING;//把地图操作类型设置为滑动平移地图
new BackgroundThread().start();
}
return true;
}
2.3.3 连击放大地图
当连续两次点击屏幕时会触发该方法。我们通过重写onDoubleTap事件监听器,并在方法体中启动相应的后台线程,向服务器发出操作参数,即可实现以点击位置为中心的放大地图功能。
public boolean onDoubleTap (MotionEvent e){
Projection p=new Projection();
//将点击处的屏幕坐标转化为地理坐标
GeoPoint geoPoint=p.fromPixels((int)e.getX(),(int)e.getY(),
width,height,zoomValue,centerX,centerY);
centerX=geoPoint.getLongitude();
centerY=geoPoint.getLatitude();
operateType=MAPDOUBLETOUCH;//将地图操作类型设置为双击放大地图
new BackgroundThread().start();
return super.onDoubleTap(e);
}
3 功能实现与应用
3.1 客户端地图开发包调用流程
本文设计并实现了一种基于Android移动客户端GIS应用框架,并形成了一个移动地图开发包。用户可以基于该开发包进行二次开发,搭建基础的地图应用,实现信息查询、地图定位、专题制图等功能。客户端工具包的调用流程如下:
1)首先获取由上述几个核心类打包生成的geomapapi.jar;
2) 在Eclipse下新建Android项目,添加libs文件夹,并把geomapapi.jar拷到libs文件夹下;
3)在新建的Activity中引入所需的类:
import com.any.geomap.MapView;
import com.any.geomap.GeoPoint;
import com.any.geomap.Projection;
4)在AndroidManifest.xml文件中添加访问网络的权限:
5)在layout文件夹下的main.xml布局文件中,添加MapView:
android:id="@+id/myMapView" /> 6)在新建的Activity中初始化MapView并调用相关方法[7],代码如下: public class GeoMapAPI extends Activity { MapView mapView; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); mapView=(MapView)findViewById(R.id.myMapView); mapView.getMap("gzmap");//显示地图,地图工作空间名为gzmap mapView.enableGesture();//允许使用手势操作 } } 我们基于这个移动地图开发包,实现了一个可在安卓移动平台上运行的上海港政地理信息系统移动版原型(图3)。上海港政地理信息系统是一个基于Web的用来管理上海港各岸线段、码头、泊位的地理信息系统。移动版原型系统主要实现了地图平移缩放、点查询及Info查询、要素的定位显示、图层控制等功能。 图3显示的位置是长江口和黄浦江,蓝色线状要素表示岸线,绿色的多边形状标识是泊位,点状标记是灯塔。用户可以通过菜单里的相应操作实现地图基本功能,还可以使用右下角的控件实现地图的缩放、通过双击地图来放大地图、滑动屏幕实现地图的平移。用户可以点击地图上某个位置来查询岸线和码头的信息,也可以根据岸线或泊位的名字来查询以及在地图上定位查询结果并高亮显示。 图3 上海港政地理信息系统移动版 [1]舒贤华.基于Android平台的手机Web地图服务设计[D].大连:大连海事大学,2009 [2]陈方圆,李治洪,谢文明,等.基于Linux 的能源与环境监测WebGIS[J].计算机工程,2011(12):247-250 [3]柯元旦,宋锐.Android 程序设计[M].北京: 北京航空航天大学出版社,2010 [4]耿东久,索岳,陈渝,等.基于Android 手机的远程访问和控制系统[J].计算机应用,2011,31(2):559-560 [5]李治洪.WebGIS原理与实践[M].北京:高等教育出版社,2011 [6]杨丰盛.Android 应用开发揭秘[M].北京: 机械工业出版社,2010 [7]公磊,周聪.基于Android 的移动终端应用程序开发与研究[J].计算机与现代化,2008(8): 85-893.2 应用案例