百度地图获取缩放级别

目前在做一个产品的大改版,主要是交互方式变化很大,尤其是增加了地图相关交互,类似于滴滴、优步首页。
map.png

开发时遇到的很大的一个问题就是缩放控制,由于界面中同时存在地图及输入覆盖层,在地图上添加标注时需要精确控制标注显示区域,使标注显示范围控制在覆盖层区域上方。

遮盖区域.jpeg

查阅android端相关api后发现缩放控制主要由此类 MapStatusUpdateFactory 间接来完成。

api.png

尝试所有方法后发现百度提供的api不能同时控制地理范围及中心点😓,
MapStatusUpdateFactory.newLatLngBounds(builder.build()) 此方法可控制所有标注显示在以地图中心为中心点的整个地图上,但无法控制中心点位置。

尝试人为构造边界点来控制显示

示例.jpeg

如上图,红色为原始点,紫色为根据当前屏幕未被覆盖区域宽高及经纬度差值比例生成的边界点,实际测试后发现 MapStatusUpdateFactory.newLatLngBounds(builder.build()) 此方法不能完美显示。

尝试获取设置中心点及缩放比例

MapStatus 此类也可控制地图。

  MapStatus mapStatus = new MapStatus.Builder()
                    .target(builder.build().getCenter())
                    .zoom(zoom)
                    .targetScreen(point).build();

            mMap.animateMapStatus(MapStatusUpdateFactory.newMapStatus(mapStatus));

FADF5D67-2020-44F9-B3B2-EC42049D7E95.png

此方法可控制中心点经纬度及中心点屏幕坐标,适合精确控制单个点。但是MapStatus.Builder 此构造类只提供了缩放级别设置,并不能设置经纬度范围,在查阅了api后发现百度并未提供根据经纬度范围获取缩放比例的api,网上给出的很多方法为通过比例尺、缩放级别计算,很不靠谱...... 遂放弃此方法。

询问了百度地图的产品和技术,告知的方法还是通过MapStatusUpdateFactory 来完成,并不能达到我们的要求,但是ios 提供了相关的方法 😓。

为了不影响开发进度,开发前期采用了人为构造边界点的方法。

在项目主流程接近完成时,又重新开始地图的开发,对百度提供的一些类进行了仔细研究。发现 MapStatusUpdateFactory 类的 MapStatusUpdateFactory 方法,也就是人为构造边界点所使用的方法:

    public static MapStatusUpdate newLatLngBounds(LatLngBounds var0) {
        if(var0 == null) {
            return null;
        } else {
            MapStatusUpdate var1 = new MapStatusUpdate(3);
            var1.d = var0;
            return var1;
        }
    }

在所构造的 MapStatusUpdate 对象在此方法mMap.animateMapStatus(MapStatusUpdateFactory.newLatLngBounds(builder.build())); 调用过程中,会调用以下方法:

 MapStatusUpdate(int var1) {
        this.a = var1;
    }

    MapStatus a(e var1, MapStatus var2) {
        if(null != var1 && null != var2) {
            switch(this.a) {
            case 1:
                return this.b;
            case 2:
                return new MapStatus(var2.rotate, this.c, var2.overlook, var2.zoom, var2.targetScreen, (LatLngBounds)null);
            case 3:
                double var3 = 0.0D;
                double var5 = 0.0D;
                double var7 = 0.0D;
                double var9 = 0.0D;
                GeoPoint var11 = CoordUtil.ll2mc(this.d.southwest);
                GeoPoint var12 = CoordUtil.ll2mc(this.d.northeast);
                var3 = var11.getLongitudeE6();
                var5 = var12.getLatitudeE6();
                var7 = var12.getLongitudeE6();
                var9 = var11.getLatitudeE6();
                float var13 = var1.a((int)var3, (int)var5, (int)var7, (int)var9, var2.a.j.right - var2.a.j.left, var2.a.j.bottom - var2.a.j.top);
                LatLng var14 = this.d.getCenter();
                return new MapStatus(var2.rotate, var14, var2.overlook, var13, var2.targetScreen, (LatLngBounds)null);
}

case 3 返回的 MapStatus 对象的构造方法为:

   MapStatus(float var1, LatLng var2, float var3, float var4, Point var5, LatLngBounds var6) {
        this.rotate = var1;
        this.target = var2;
        this.overlook = var3;
        this.zoom = var4;
        this.targetScreen = var5;
        if(this.target != null) {
            this.b = CoordUtil.ll2mc(this.target).getLongitudeE6();
            this.c = CoordUtil.ll2mc(this.target).getLatitudeE6();
        }

        this.bound = var6;
    }

注意这个构造方法的 var4 参数,此参数为缩放级别。

传入的值便是由此方法计算出来的:
float var13 = var1.a((int)var3, (int)var5, (int)var7, (int)var9, var2.a.j.right - var2.a.j.left, var2.a.j.bottom - var2.a.j.top);

这个类是什么鬼

public class e implements b {
     public e(Context var1, String var2) {
       this.A = var1;
       this.f = new ArrayList();
       this.an = var2;
   }
}

本想自己实例化一个对象,发现找不到这个 an 到底是什么东西...... 直接实例化的方法失败了。

在经过一番查找后,在 BaiduMap 中找到了这个类的实例

    private e i;
    BaiduMap(af var1) {
        this.j = var1;
        this.i = this.j.b();
        this.d = ad.b;
        this.c();
    }

    BaiduMap(j var1) {
        this.h = var1;
        this.i = this.h.a();
        this.d = ad.a;
        this.c();
    }

并且是在 BaiduMap 实例化时就被实例化了!!!

这个属性是 private 的,并不能直接获取,需要通过反射获取:

        Field eField = null;
        eField = BaiduMap.class.getDeclaredField("i");
        eField.setAccessible(true);
        com.baidu.platform.comapi.map.e zoomUtils = (com.baidu.platform.comapi.map.e) eField.get(baiduMap);

获取到这个类的实例后,就可以调用它的方法计算缩放级别了:

public float a(int var1, int var2, int var3, int var4, int var5, int var6) {
        if(!this.i) {
            return 12.0F;
        } else if(this.g == null) {
            return 0.0F;
        } else {
            Bundle var7 = new Bundle();
            var7.putInt("left", var1);
            var7.putInt("right", var3);
            var7.putInt("bottom", var4);
            var7.putInt("top", var2);
            var7.putInt("hasHW", 1);
            var7.putInt("width", var5);
            var7.putInt("height", var6);
            return this.g.c(var7);
        }
    }

传入相关参数

       GeoPoint southwest = CoordUtil.ll2mc(builder.build().southwest);
        GeoPoint northeast = CoordUtil.ll2mc(builder.build().northeast);
        double left = southwest.getLongitudeE6();
        double top = northeast.getLatitudeE6();
        double right = northeast.getLongitudeE6();
        double bottom = southwest.getLatitudeE6();
        float zoom = zoomUtils.a((int) left, (int) top, (int) right, (int) bottom, width, height);

此时就得到缩放级别了,最后的 widthheight 为显示区域 。

上述计算方法经过层层调用,最终会调用native方法

public class JNIBaseMap {
    public long a;

    public JNIBaseMap() {
    }

    public native float GetZoomToBound(long var1, Bundle var3);
}

实际测试中,比人为构造边界点的效果好很多,但是存在宽高区域控制无效的情况,目前暂无更好的办法,只能期望百度开放相关的接口了......

最后附上完整代码


 public static float getFitZoom(BaiduMap baiduMap, LatLngBounds.Builder builder, int width, int height) throws Exception {
        Field eField = null;
        eField = BaiduMap.class.getDeclaredField("i");
        eField.setAccessible(true);
        com.baidu.platform.comapi.map.e zoomUtils = (com.baidu.platform.comapi.map.e) eField.get(baiduMap);
        GeoPoint southwest = CoordUtil.ll2mc(builder.build().southwest);
        GeoPoint northeast = CoordUtil.ll2mc(builder.build().northeast);
        double left = southwest.getLongitudeE6();
        double top = northeast.getLatitudeE6();
        double right = northeast.getLongitudeE6();
        double bottom = southwest.getLatitudeE6();
        float zoom = zoomUtils.a((int) left, (int) top, (int) right, (int) bottom, width, height);
        Log.e("zoom",zoom+"");
        return zoom;
    }

注:百度地图版本4.5.2

推荐阅读更多精彩内容

  • 因为要做一个交通仿真项目,需要用到这个地图库,但是查询官方API麻烦,而且这个地图框架的API做的用起来确实太麻烦...
    虚幻的锈色阅读 17,299评论 3 11
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 76,500评论 13 117
  • 百度地图API 开始学习百度地图API最简单的方式是看一个简单的示例。以下代码创建了一个520x340大小的地图区...
    JamHsiao_aaa4阅读 1,291评论 2 3
  • pdf下载地址:Java面试宝典 第一章内容介绍 20 第二章JavaSE基础 21 一、Java面向对象 21 ...
    王震阳阅读 79,598评论 26 513
  • 【 申请密钥AndroidSDK:应用程序包名+数字签名 As查看数字签名:cmd-----cd .android...
    征程_Journey阅读 904评论 0 5