百度、google、高德 地图比例尺功能实现(算法&&地图分辨率和zoomlevel之间的关系)



一、什么是比例尺?

比例尺是表示图上距离比实地距离缩小的程度,因此也叫缩尺。用公式表示为:比例尺=图上距离/实地距离。

例如地图上1厘米代表实地距离500千米,可写成:1∶50,000,000或写成:1/50,000,000。

在当前移动端或是web端地图上显示:

baidu   google

图中的公里或英里数会随着黑条宽度的改变而改变,并且它的数值和地图当前区域的地图图层分辨率和缩放等级有关。浅显的来看,拿百度地图举例,百度地图移动端地图缩放等级分为:3-21,也就是19个等级,在同一缩放等级下,无论你移动到世界的每一个角落,比例尺的数值,公里\米 都是一样的。反之,googl地图就不一样,在同一缩放等级下,移动到世界不同的区域,显示的公里\ 英里 数值是不一样的,以赤道为中心,向两级延伸。google真正做到了球形的地球,而百度缩放到世界地图,你会看到整个地球是一张纸:

先看看google地图:


以下是百度地图:





二、移动端的实现

大家无论使用ios还是android的,会发现,三个地图api,有的提供了比例尺组件,有的没有,所以,只能我们自己来实现,接下来我就说一下如何实现,以百度地图android版(v2.1.2)为例。

首先简单说一下实现原理:

获取屏幕上两个点的位置坐标(中心点为起点,终点的x坐标为设计的比例尺最宽宽度),通过这两个点的图上坐标转换为真实经纬度,然后使用地图api提供的两点之间真是距离的函数,算出真实距离,即为当前缩放等级下,代表的大概比例尺数值。

2-1,定义一个要显示比例尺数值的数组:

	public static final int[] SCALE = { 1, 20, 50, 100, 200, 500, 1000, 2000,
		5000, 10000, 20000, 25000, 50000, 100000, 200000, 500000, 1000000,
		2000000, 5000000 };

2-2 在当前地图Activity中实现 MKMapViewListener 接口,覆盖其中的方法,并设置监听:

mMapView.regMapViewListener(mBMapManager, this);

	@Override
	public void onClickMapPoi(MapPoi arg0) {
	}

	@Override
	public void onGetCurrentMap(Bitmap arg0) {
	}

    //当地图完成缩放,移动等动画效果后,回调此方法
	@Override
	public void onMapAnimationFinish() {
	showScaleView();
		
	}

	@Override
	public void onMapMoveFinish() {
	}

2-3 声明并实例化组件:

    protected View mScaleView;
	protected int mScaleMaxWidth;
	protected int mScaleMaxHeight;
	protected TextView mScaleTextView;
	protected ImageView mScaleImageView;
	    mScaleView = getLayoutInflater().inflate(R.layout.main_scale, null);
		LayoutParams layoutParams = new LayoutParams(LayoutParams.WRAP_CONTENT,
				LayoutParams.WRAP_CONTENT);
		layoutParams.setMargins(10, 10, 0, 0);
		mScaleView.setLayoutParams(layoutParams);

		mScaleTextView = (TextView) mScaleView.findViewById(R.id.scale_text);
		mScaleImageView = (ImageView) mScaleView.findViewById(R.id.scale_image);

2-4 main_scale,xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:orientation="vertical" >

    <TextView
        android:id="@+id/scale_text"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:singleLine="true"
        android:textColor="@color/text_black" />

    <ImageView
        android:id="@+id/scale_image"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="@drawable/scale"
        android:contentDescription="@null" />

</LinearLayout>

2-5,showScaleView()

	/** 计算两处经纬度的实际距离并换算成实际比例尺单位 */
	public void showScaleView() {
		
		//获取设置默认屏幕宽度480
		int default_screen_width = this.getResources()
				.getDimensionPixelSize(R.dimen.default_screen_width);
		//获取设置默认屏幕高度800
		int default_screen_height = this.getResources()
				.getDimensionPixelSize(R.dimen.default_screen_height);
		//mScaleMaxWidth = 默认屏幕宽度四分之一
		mScaleMaxWidth = default_screen_width>>2;
		//mScaleMaxHeight = 默认屏幕高度二分之一
		mScaleMaxHeight = default_screen_height>>1;

		//转换为起点的经纬度GeoPoint
		GeoPoint fromGeopoint = mMapView.getProjection().fromPixels(0,
				mScaleMaxHeight);
		//转换为终点的经纬度GeoPoint
		GeoPoint toGeopoint = mMapView.getProjection().fromPixels(
				mScaleMaxWidth, mScaleMaxHeight);
		//通过getDistance函数得出两点间的真实距离
		double distance = DistanceUtil.getDistance(fromGeopoint, toGeopoint);

		String discripition = null;
		int dis = 0;
		int width = 20;
		//出于百度地图缩放最小为世界地图时,以纸的形式展现,这里强制规定如果zoomlevel=最高级
		//比例尺数值恒定wie:2000公里,黑条宽度恒定为52像素,缩放最大时同样的道理,如果是google
		//地图,不需要if和 else if
		if (mMapView.getZoomLevel() == 19) {
			dis = Constants.SCALE[1];
			width = 52;
		} else if (mMapView.getZoomLevel() == 3) {
			dis = Constants.SCALE[17];
			width = 80;
		} else {

			//真实距离和数组中相近的两个值循环比较,以小值为准,得出规定好的比例尺数值赋值给dis
			for (int j = 1; j < Constants.SCALE.length; j++) {
				if (Constants.SCALE[j - 1] <= distance
						&& distance < Constants.SCALE[j]) {
					dis = Constants.SCALE[j - 1];
					break;
				}
			}
			//比例尺黑条的宽度 =(dis*默认屏幕宽度四分之一)/真实的距离
			width = (int) (dis * mScaleMaxWidth / distance);
		}

		//如果比例尺数值大于1000,则discripition = 2500 km(公里),否则 为 900 m(米)
		if (dis >= 1000) {
			discripition = dis / 1000 + getString(R.string.units_km);
		} else {
			discripition = dis + getString(R.string.units_m);
		}

		//设置TextView组件要显示的比例尺数值
		mScaleTextView.setText(discripition);
		//设置比例尺黑条ImageView的的长度
		android.view.ViewGroup.LayoutParams lp = mScaleImageView
				.getLayoutParams();
		lp.width = width;
		mScaleImageView.setLayoutParams(lp);

	}

至此,代码部分就结束了,咱们看一下运行效果




如果你想要实时的动态效果,也就是说双指触控到屏幕(缩放地图)时,(手指未离开)就不断变化比例尺,就不能写在onMapAnimationFinish回调函数内了,因为它不是实时调用,只有当每次动画结束后(双指也离开屏幕了),才会调用;那就覆盖onTouchEvent方法:

	@Override
	public boolean onTouchEvent(MotionEvent arg0) {
		showScaleView();

		return super.onTouchEvent(arg0);
	}
这样,每当手指触摸到屏幕,就会实时调用算法啦。不过看样子cpu应该会很累的。


至于google和高德地图,把showScaleView()写在其地图缩放的回调函数内即可,google的好像是 onCameraChange();



三、结尾

到此,比例尺的功能就被我们自己实现了,这个算法不知道算不算严格的科学,但是至少应该不会差很多,如果谁有同样的兴趣,欢迎和我一起探讨噢。




yang

20130718




阅读更多
想对作者说点什么?

博主推荐

换一批

没有更多推荐了,返回首页