http://api.tianditu.com/api-new/home.html
开发完回头修改了这个文章。
(有需求可参看《入门Android开发--ArcGis结合天地图实现地图建筑图层》)
天地图有国家和省的版本,地图显示详细度不一样(省的详细),功能不一样(国家的强)
所以根据需求,用了国家天地图(目前知道省的内部天地图Android客户端功能很强,但是没有sdk)
无需注册(百度地图说防止“balabla”,所以有个key),
在项目Libraries里添加tiandituapi2.2.2.jar,将libMapEngine.so复制到工程目录下的libs\armeabi下,再在android工程中引用jar和so文件
天地图里类参考中,mapview的一些类,在demo中有出现,但是说明里没有找着= =
//天地图常见的小问题就不多说了。可以百度类似的错误进行解决。
//大问题有:
//(路线规划的回调结果经常出错),一看错误类。。。-1为不支持的地图类型。。。呵呵
// 请看问题4
目前的需求和解决思路:
1、获取两个坐标点的“直线”距离,没有自带的方法。
答:网上找的(和百度地图的距离差不多吧):
double s =R*Math.acos(Math.cos(lat1*pi/180) *Math.cos(lat2*pi/180)*Math.cos(lng1*pi/180 -lng2*pi/180)+
Math.sin(lat1*pi/180 )*Math.sin(lat2*pi/180));
2、获取两个坐标点的路程距离。
答:用驾车规划里的回调结果,获取路程长度。或者使用网页服务
http://www.tianditu.cn/query.shtml , POST方式:postStr={'orig':'120.957149,28.112149','dest':'120.957854,28.111113','style':'0','mid':''}&type=search
获取一个xml格式的结果
3、路线规划的结果需要恰好最合适显示在地图视图中间,
答:首先用自带的方法,确定返回结果的中心点,和视图的经纬度范围。
求经纬度范围有个自带的zoomToSpan方法,但是我这边的点位,起码试出了一个不能合理显示(有一个之后就没试其他的了)
然后就是自己写一个,这也是花了最多时间的部分。
思路大概就是先获取地图的缩放比例,经纬度范围(都是可以直接用的方法)
然后创建一个比例数组,和各个比例之间的比例尺的换算比例数组(如下代码块部分)
通过地图视图显示的经纬度乘以换算比例,和路线规划返回的经纬度点的经纬度范围进行比较,得到相应的缩放比例。
//缩放等级对应,如果地图未设置缩放等级,则为4km,即12。 //minLevle为5,如果低于则设为5;maxLevel为18,如果高于则设为18 //5为400km,6为200km,7为92km,8为48km,9为24km,(基本盖住温州了) //10为16km,11为8km,12为4km,13为2km,14为1km,15为500m, //16为240m,17为120m,18为60m, int[] level= {5,6,7,8,9,10,11,12,13,14,15,16,17,18}; double[] multiple= {0.5,0.46,0.52,0.5,0.67,0.5,0.5,0.5,0.5,0.5,0.48,0.5,0.5};
4、如果有两个activity都使用了地图,两张地图会互相影响(包括地图图层的显示、比例和长宽)
答:目前没有好的办法,只能把第二张地图设置为第一张地图一样的设置。
5、结合3、4。因为我们先进入一个默认地图(全屏,假设100,map_def),然后点击目标点,
进入路线规划,因为有其他面板,所以地图高度只用了部分(假设80, map_route)
然后返回def的时候,def只会显示80的部分,下面20为空。
如果route部分因为适应缩放改变了比例尺,那么def地图也会变化成相应的比例尺。
(其实这个20,是通过动态计算的方法。(网上有计算组件高度的方法,但是我不知道怎么结合进这里,我用的是计算字符高度和空白高度的方法)
为了说明方便,直接使用了20。)
答:还不懂这个地图的原理,所以只好把route的地图也设置成100,不过20部分被面板遮盖。
然后在activity返回的时候设置route的缩放级别和def的一样(route通过intent获取def的缩放级别,在返回键和设置的back按钮点击事件中设置)
这样会有两个新的问题:地图的视图下移20/2(让画出来的线显示在下方的80中),经纬度范围为原纬度范围的4/5
这里下移20/2又需要换算成经纬度范围,(注意这里是比例尺改变之后的偏移,所以需要乘以不同的比例)
总结一下方法:计算比例尺等级的方法同3,然后通过比较旧的比例尺范围,还是3中的两个数组,进行比例换算,即可以算出新的比例尺下,20/2对应的纬度范围值
通过将中心点的纬度坐标加上这个20/2对应的纬度范围值,即可完成中心点偏移操作。
6、在解决上面的问题的时候出现了地图无法缩放和移动的问题。
答:因为我把更改地图状态的函数写在了,overlay类里的重载draw方法中,
这个地图的机制大致是这样的:在更改地图图层状态的时候,通知所有的图层,调用draw方法,更改对应图层装态
(比如绘制路线的图层,就是在路线规划的回调中,将地图的中心点设置了一下之后,才调用了draw方法,开始绘制)
而产生这个问题的原因是,我把设置中心点放在了draw方法里,那每次拖动地图的时候,都会重新调用这个设置中心点的方法。
所以加了一个isFirstDraw的判断。即可解决。
7、代码怎么写呢。嘿嘿。主要的工具代码如下:(看不懂没事啊。不难,就是写了好久,放一下)
//缩放等级对应,如果地图未设置缩放等级,则为4km,即12。 //minLevle为5,如果低于则设为5;maxLevel为18,如果高于则设为18 //5为400km,6为200km,7为92km,8为48km,9为24km,(基本盖住温州了) //10为16km,11为8km,12为4km,13为2km,14为1km,15为500m, //16为240m,17为120m,18为60m, //在线路导航布局下, public class ZoomLevelUtil { //每次点击按钮的时候,比例等级都是不一定的。 //所以需要判断 int[] level={5,6,7,8,9,10,11,12,13,14,15,16,17,18}; double[] multiple={0.5,0.46,0.52,0.5,0.67,0.5,0.5,0.5,0.5,0.5,0.48,0.5,0.5}; GeoPoint point; Activity _activity; MapView mapView; int before_zoomLevel;//记录刚获取时视图的比例设置 int after_zoomLevel;//记录最后确定的比例设置 public ZoomLevelUtil(Activity activity,GeoPoint point,MapView mapView){ this.point=point; _activity=activity; this.mapView=mapView; before_zoomLevel=mapView.getZoomLevel();//获取地图当前比例等级 after_zoomLevel=mapView.getZoomLevel(); } //解析进入时,每个地图视图被隐藏的高度(即上部面板高度对应的Lat值) public int parseSpan(int SpanE6){ // TODO Auto-generated method stub //这个例子需要减去一部分,为22dp+4text+20sp DisplayMetrics metric = new DisplayMetrics(); _activity.getWindowManager().getDefaultDisplay().getMetrics(metric); int height = metric.heightPixels; //屏幕高度 float scale=_activity.getResources().getDisplayMetrics().density;//屏幕密度 int title_height=DensityUtil.dip2px(_activity, 48); height-=scale*title_height; //获取默认单个字符高度 TextPaint paint = new TextPaint(); FontMetrics fm = paint.getFontMetrics(); //Math.ceil(fm.descent - fm.top) + 2; int deftext_Height=(int)Math.ceil(fm.descent - fm.ascent) ; //获取20sp字符单个高度 paint.setTextSize(20); fm = paint.getFontMetrics(); int text_20_Height=(int)Math.ceil(fm.descent - fm.ascent) ; int margin=DensityUtil.dip2px(_activity, 22);//上下总空余的距离22dp return (int)(SpanE6*(scale*(double)(3*deftext_Height+text_20_Height+margin)/(double)height)); } //获取新的中心点坐标 //需要先执行getAfterZoomLevel() public GeoPoint getFreshPoint(){ int after_addLatSpan=0; //获取点击时,应该减小的距离 int before_addLatSpan=parseSpan(mapView.getLatitudeSpan()); //获取从before_zoomLevel转到after_zoomLevel,所需要的比例 after_addLatSpan=(int)((double)before_addLatSpan*getScale(before_zoomLevel,after_zoomLevel)); //要把地图往下移,其实是把中心点上调 int after_LatSpan=point.getLatitudeE6()+after_addLatSpan/2; GeoPoint new_P=new GeoPoint(after_LatSpan,point.getLongitudeE6()); // GeoPoint point = mResult.getCenterPoint(); // int new_lat=point.getLatitudeE6()+addLngSpan/2; return new_P; } //计算,最终zoomLevel和传入的zoomLevel时,两者距离的比例 //需要先执行getAfterZoomLevel() private double getScale(int before_zoomLevel,int after_zoomLevel) { // TODO Auto-generated method stub double scale=1; //获取处理前等级在列表中的位置 int i=0; for(;i<level.length;i++){ if(level[i]==before_zoomLevel){ break; } } //获取处理后等级在列表中的位置 int j=0; for(;j<level.length;j++){ if(level[j]==after_zoomLevel){ break; } } if(i<j){ for(;i<j;i++) scale*=multiple[i]; }else if(i>j){ for(;i>j;){ // i--; scale/=multiple[--i]; } } return scale; } public int getAfterZoomLevel(int latSpanE6,int lngSpanE6){ // int zoomLevel=15;//默认为15(15的比例比较适中,使用18或者12都有点不合适) int before_zoomLevel=mapView.getZoomLevel(); int maxLevel=mapView.getMaxZoomLevel(); int minLevel=mapView.getMinZoomLevel(); //获取屏幕经纬度范围, int mapLatSpan=mapView.getLatitudeSpan(); int mapLngSpan=mapView.getLongitudeSpan(); //根据比例换算实际显示的高度 mapLatSpan-=parseSpan(mapLatSpan); if(maxLevel!=18||minLevel!=5){ Toast.makeText(_activity, "比例等级变化,校准失败", Toast.LENGTH_SHORT).show(); return before_zoomLevel; } //获取等级在列表中的位置 int i=0; for(;i<level.length;i++){ if(level[i]==before_zoomLevel){ break; } } //设置经度比例和纬度比例 int latLevel=before_zoomLevel; int lngLevel=before_zoomLevel; int j=i; if(mapLatSpan<=latSpanE6){ for(;mapLatSpan<=latSpanE6;){ if(j==0) break; mapLatSpan/=multiple[j--]; } }else{ for(;mapLatSpan>latSpanE6;j++){ if(j==(level.length-1)) break; mapLatSpan*=multiple[j]; } j--; } latLevel=level[j]; j=i; if(mapLngSpan<=lngSpanE6){ for(;mapLngSpan<=lngSpanE6;){ if(j==0) break; j--; mapLngSpan/=multiple[j]; } }else{ for(;mapLngSpan>lngSpanE6;j++){ if(j==(level.length-1)) break; mapLngSpan*=multiple[j]; } j--; } lngLevel=level[j]; after_zoomLevel=latLevel<lngLevel?latLevel:lngLevel; return after_zoomLevel; } }