本文主要讲解如何通过百度地图API搜索得到一个城市里的所有POI。这里有必要对“所有”这个词进行强调一下,以便引起重视,之所以这样说,是因为在搜索POI时,默认仅返回一页的搜索结果10条,那么如何才能得到所有的搜索结果呢?其实baidu map api是提供了相关的方法,但我发现有相当多的网友都在问这个问题,所以有必要讲解演示一下。
先讲一下什么称之为“城市POI搜索”?它与我们在上一篇文章([011] 百度地图API之POI搜索-发现你身边的兴趣点,如超市、餐厅、ATM...(Android))中了解到的POI搜索有什么区别呢?
上一篇文章中所调用的是地图API的“周边POI搜索”服务,即检索周围多少米以内的POI;而本章所要调用的是地图API的“城市POI搜索”服务,即检索某个城市中所有的POI。如果你看完这两篇文章后,你会发现仅仅是调用的方法不同而以,搜索结果的处理方法是同一个方法,搜索结果的处理代码也是完全一样的。
下面将给出城市POI搜索的一个完整示例,并且会讲解如何才能获取到所有的搜索结果。
1)布局文件res/layout/poi_city_search.xml
2)继承了com.baidu.mapapi.MapActivity的Activity类
package com.liufeng.baidumap; import android.app.AlertDialog; import android.content.DialogInterface; import android.os.Bundle; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.EditText; import com.baidu.mapapi.BMapManager; import com.baidu.mapapi.MKAddrInfo; import com.baidu.mapapi.MKDrivingRouteResult; import com.baidu.mapapi.MKPoiInfo; import com.baidu.mapapi.MKPoiResult; import com.baidu.mapapi.MKSearch; import com.baidu.mapapi.MKSearchListener; import com.baidu.mapapi.MKTransitRouteResult; import com.baidu.mapapi.MKWalkingRouteResult; import com.baidu.mapapi.MapActivity; import com.baidu.mapapi.MapController; import com.baidu.mapapi.MapView; import com.baidu.mapapi.PoiOverlay; public class PoiSearchInCityActivity extends MapActivity { // 定义地图引擎管理类 private BMapManager mapManager; // 定义搜索服务类 private MKSearch mMKSearch; private MapView mapView; private MapController mapController; private EditText keyWordEditText; private Button queryButton; private static StringBuilder sb; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.poi_city_search); // 初始化MapActivity mapManager = new BMapManager(getApplication()); // init方法的第一个参数需填入申请的API Key mapManager.init("285B415EBAB2A92293E85502150ADA7F03C777C4", null); super.initMapActivity(mapManager); mapView = (MapView) findViewById(R.id.map_View); // 设置地图模式为交通地图 mapView.setTraffic(true); // 设置启用内置的缩放控件 mapView.setBuiltInZoomControls(true); // 取得地图控制器对象,用于控制MapView mapController = mapView.getController(); // 设置地图默认的缩放级别 mapController.setZoom(10); // 设置每页返回的POI数,默认为10,取值范围1-50 MKSearch.setPoiPageCapacity(10); // 初始化MKSearch mMKSearch = new MKSearch(); mMKSearch.init(mapManager, new MySearchListener()); keyWordEditText = (EditText) findViewById(R.id.keyword_edittext); queryButton = (Button) findViewById(R.id.query_button); queryButton.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { //每次搜索前先前sb中的内容清空 sb = new StringBuilder(); String keyWord = keyWordEditText.getText().toString().trim(); // 搜索贵阳地区的沃尔玛 mMKSearch.poiSearchInCity("贵阳", keyWord); } }); } @Override protected boolean isRouteDisplayed() { return false; } @Override protected void onDestroy() { if (mapManager != null) { // 程序退出前需调用此方法 mapManager.destroy(); mapManager = null; } super.onDestroy(); } @Override protected void onPause() { if (mapManager != null) { // 终止百度地图API mapManager.stop(); } super.onPause(); } @Override protected void onResume() { if (mapManager != null) { // 开启百度地图API mapManager.start(); } super.onResume(); } /** * 实现MKSearchListener接口,用于实现异步搜索服务 * * @author liufeng */ public class MySearchListener implements MKSearchListener { /** * 根据经纬度搜索地址信息结果 * * @param result 搜索结果 * @param iError 错误号(0表示正确返回) */ @Override public void onGetAddrResult(MKAddrInfo result, int iError) { } /** * 驾车路线搜索结果 * * @param result 搜索结果 * @param iError 错误号(0表示正确返回) */ @Override public void onGetDrivingRouteResult(MKDrivingRouteResult result, int iError) { } /** * POI搜索结果(范围检索、城市POI检索、周边检索) * * @param result 搜索结果 * @param type 返回结果类型(11,12,21:poi列表 7:城市列表) * @param iError 错误号(0表示正确返回) */ @Override public void onGetPoiResult(MKPoiResult result, int type, int iError) { if (result == null) { return; } // 清除地图上已有的所有覆盖物 mapView.getOverlays().clear(); // PoiOverlay是baidu map api提供的用于显示POI的Overlay PoiOverlay poioverlay = new PoiOverlay(PoiSearchInCityActivity.this, mapView); // 设置搜索到的POI数据 poioverlay.setData(result.getAllPoi()); // 在地图上显示PoiOverlay(将搜索到的兴趣点标注在地图上) mapView.getOverlays().add(poioverlay); if(result.getNumPois() > 0) { // 设置其中一个搜索结果所在地理坐标为地图的中心 MKPoiInfo poiInfo = result.getPoi(0); mapController.setCenter(poiInfo.pt); } sb.append("共搜索到").append(result.getNumPois()).append("个POI/n"); // 遍历当前页返回的POI(默认只返回10个) for (MKPoiInfo poiInfo : result.getAllPoi()) { sb.append("名称:").append(poiInfo.name).append("/n"); //sb.append("地址:").append(poiInfo.address).append("/n"); //sb.append("经度:").append(poiInfo.pt.getLongitudeE6() / 1000000.0f).append("/n"); //sb.append("纬度:").append(poiInfo.pt.getLatitudeE6() / 1000000.0f).append("/n"); } // 通过AlertDialog显示当前页搜索到的POI new AlertDialog.Builder(PoiSearchInCityActivity.this) .setTitle("搜索到的POI信息") .setMessage(sb.toString()) .setPositiveButton("关闭", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int whichButton) { dialog.dismiss(); } }).create().show(); } /** * 公交换乘路线搜索结果 * * @param result 搜索结果 * @param iError 错误号(0表示正确返回) */ @Override public void onGetTransitRouteResult(MKTransitRouteResult result, int iError) { } /** * 步行路线搜索结果 * * @param result 搜索结果 * @param iError 错误号(0表示正确返回) */ @Override public void onGetWalkingRouteResult(MKWalkingRouteResult result, int iError) { } } }
3)AndroidManifest.xml中的配置
4)运行结果
程序运行起来后,我们输入关键词“ktv”进行搜索,而在代码中我们设置了搜索城市为“贵阳”,即我们要搜索贵阳市所有的KTV。搜索完成后,首先会弹出一个对话框,如上图所示,显示了搜索到了POI总数及每一个POI名称,点击“关闭”按钮后,可以看到搜索到的ktv所在位置也在地图上进行了标注。
到目前为止,一切看起来很正常。然而,真的是这样吗?请仔细看上面第一张图中显示的搜索结果数为60,即在贵阳市总共个60家ktv,但是下面却只显示了10家ktv名称,并且第二张图中地图中也只标注了10家ktv,这到底是为什么呢?其它50家ktv结果去哪了?原因是这样的:
一次搜索所得到的POI数有时候可能有成千上万个,比如你搜索“餐厅”试试,试想如果一次性从百度地图服务器上返回上万条搜索结果数据到客户的手机上将会是一种什么样的情况?所以百度地图API对一次性返回的搜索结果进行了限制--一次性最多只能返回50条搜索结果,默认情况下一次性返回10条搜索结果。我们可以通过下面的方法去改变一次性返回的搜索结果数:
// 设置每页返回的POI数,默认为10,取值范围1-50
MKSearch.setPoiPageCapacity(10);
但最大也只能设置为50。那么怎么才能一次性得到所有的搜索结果呢?怎么才能得到上面示例中的60家ktv的信息呢?让我们来改一下上面Activity中处理搜索结果的方法onGetPoiResult,其实所有代码都保持不变。将onGetPoiResult方法内的处理代码修改为:
/** * POI搜索结果(范围检索、城市POI检索、周边检索) * * @param result 搜索结果 * @param type 返回结果类型(11,12,21:poi列表 7:城市列表) * @param iError 错误号(0表示正确返回) */ @Override public void onGetPoiResult(MKPoiResult result, int type, int iError) { if (result == null) { return; } // 清除地图上已有的所有覆盖物 mapView.getOverlays().clear(); // PoiOverlay是baidu map api提供的用于显示POI的Overlay PoiOverlay poioverlay = new PoiOverlay(PoiSearchInCityActivity.this, mapView); // 设置搜索到的POI数据 poioverlay.setData(result.getAllPoi()); // 在地图上显示PoiOverlay(将搜索到的兴趣点标注在地图上) mapView.getOverlays().add(poioverlay); // 如果当前页的索引为0,表示第一页搜索结果 if(result.getPageIndex() == 0) { sb.append("共搜索到").append(result.getNumPois()).append("个POI/n"); } // 遍历当前页返回的POI(默认只返回10个) for (MKPoiInfo poiInfo : result.getAllPoi()) { sb.append("名称:").append(poiInfo.name).append("/n"); //sb.append("地址:").append(poiInfo.address).append("/n"); //sb.append("经度:").append(poiInfo.pt.getLongitudeE6() / 1000000.0f).append("/n"); //sb.append("纬度:").append(poiInfo.pt.getLatitudeE6() / 1000000.0f).append("/n"); } if(result.getPageIndex() < result.getNumPages()-1) { mMKSearch.goToPoiPage(result.getPageIndex() + 1); } // 如果是最后一页,显示搜索结果 else if(result.getPageIndex() == result.getNumPages()-1) { // 设置其中一个搜索结果所在地理坐标为地图的中心 MKPoiInfo poiInfo = result.getPoi(0); mapController.setCenter(poiInfo.pt); // 通过AlertDialog显示所有搜索到的POI new AlertDialog.Builder(PoiSearchInCityActivity.this) .setTitle("搜索到的POI信息") .setMessage(sb.toString()) .setPositiveButton("关闭", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int whichButton) { dialog.dismiss(); } }).create().show(); } }
再来看一下运行结果:
怎么样?搜索到的60家ktv信息一次性全部显示出来了吧。关键在于下面这3行代码:
if(result.getPageIndex() < result.getNumPages()-1) {
mMKSearch.goToPoiPage(result.getPageIndex() + 1);
}
服务器是以分页的形式分多次向我们返回搜索结果的,result.getPageIndex()得到的是当前页码(从0开始)。这3行代码的含义是:如果当前页不是最后一页,那么就转到下一页(goToPoiPage方法是作用是跳转到搜索结果的第几页),每次调用goToPoiPage方法,都会再次触发搜索请求,也就会再次回调执行onGetPoiResult方法;这和网页中的服务器端分页是一个道理,即每次点击“上一页”、“下一页”都会再一次提交查询请求。这也就是为什么我们要采用static类型的StringBuilder对象来保存搜索结果而不是在onGetPoiResult方法中定义一个局部变量,并且还要在每次点击“搜索”按钮后,重新创建一个新的StringBuilder对象来存储搜索结果。
完!