百度地图行政区划卡顿优化

最近做了一个百度地图相关的项目,其中会有显示全国所有省级行政区划的需求,但是在调用 Boundary 查询并绘制之后出现了很严重的性能问题:

可以看到拖动卡顿十分严重,没几秒帧数就从十几掉到了六。看一下内存分析:

优化前的内存占用

164M,这个占用在 vue 应用中更夸张,吃到上 G 都不是梦。网上搜了搜没找到类似的优化方案,于是自己写了个,优化之后基本可以跑满 30 帧,而且内存占用也只剩下了零头:

优化后的内存占用

优化逻辑并不复杂,下面介绍一下,完整的 demo 贴在文末,需要可自取。

优化逻辑

下面先放出来显示行政区划的具体流程:

const map = new BMapGL.Map('container')
const bdary = new BMapGL.Boundary()

// 所有省级行政区
const ALL_PROVINCE = [
    '北京市', '天津市', '河北省', '山西省', '内蒙古自治区', '辽宁省', '吉林省',
    '黑龙江省', '上海市', '江苏省', '浙江省', '安徽省', '福建省', '江西省', '山东省', '河南省', '湖北省',
    '湖南省', '广东省', '广西壮族自治区', '海南省', '重庆市', '四川省', '贵州省', '云南省', '西藏自治区',
    '陕西省', '甘肃省', '青海省', '宁夏回族自治区', '新疆维吾尔自治区', '香港', '澳门', '台湾省'
];

// 遍历所有省级行政区名称进行查询
ALL_PROVINCE.forEach(key => bdary.get(key, data => {
    // data.boundaries 是个数组,包含所有查询到的区域
    data.boundaries.forEach(bounder => {
        const ply = new BMapGL.Polygon(bounder, {
            strokeWeight: 0.2,
            fillColor: '#ffef7d',
            fillOpacity: 0.8
        })

        map.addOverlay(ply)
    })
}))

这里有两个点需要注意,首先是使用 Boundary 进行查询的时候,查询的结果并不是唯一的,他是一个数组,这一点 官方文档 写的不是很清楚,但也看得出来。为什么会这样呢。主要是因为一个地区并不一定只有一个多边形,例如沿海省份,会包含一些相关的海岛、某些省份也会有一些飞地,这些都是独立的多边形,所以会返回一个数组。

其次是每个边界信息都是一个字符串,包含许多由 ; 分隔的坐标。可以直接将这个字符串传递给 BMapGL.Polygon 直接生成对应的多边形。

1、那么卡顿和内存占用过高的原因是什么呢?

  • 拖动卡顿:每个行政区划的多边形太多,一个沿海省份可能会包含几十上百个海岛。这些绘制成独立的多边形会直接拉高重绘用时。
  • 内存占用过高:行政区划的边界精度过高,一个省份的边境基本都会包含 1 ~ 3 万个边界点。

2、如何解决?

如果你也觉得上面代码没问题的话,那么说明你和我一样都被网上的教程文章带跑偏了。

不需要遍历 data.boundaries 生成多个多边形!应该直接 new BMapGL.Polygon(data.boundaries) 生成一个多边形!

网上几乎所有的教程都告诉你应该遍历 boundaries 依次生成多边形,这是不对的!思考一下,如果一个区域里有别人的飞地,那么应该如何显示?正确的做法是把这一块挖掉,生成一个“镂空”的多边形。而如果遍历生成的话,会产生一个叠加的多边形!

我找到唯一一个说明这个特性的是 官方示例 demo - 镂空面绘制,真是讽刺(然而 官方类参考 里也没有写他支持这么搞 )。

而针对边界点过于精细的问题,直接等距挑点即可,这里要注意,如果一个多边形边界点太少的话就不要再挑拣了,不然会出现区域变成三角形或者消失的问题(例如海南省和台湾省):

// 获取优化后的边界 bounder
const getOptimizationBounder = function (bounder) {
    const splittedBounder = bounder.split(';');
    // 如果总数量小于 100 个点的话就不进行缩减了,不然会很丑(例如海南台湾,会变成一个三角形或者消失掉)
    if (splittedBounder.length < 100) return bounder;

    // 等距筛选点位
    const minBounder = splittedBounder.filter((point, index) => !(index % 25));
    return minBounder.join(';');
}

完整 DEMO

下面是完整的示例,记得填写你的 key,切换里边的 USE_GOOD_DRAW 来查看优化前后的效果对比。

<html lang="zh-CN">
    <script type="text/javascript" src="http://api.map.baidu.com/getscript?type=webgl&v=3.0&ak=你的key"></script> 
    <body id="container" style="height: 100vh; width: 100vw;"></body>
</html>

<script>
// 置为 false 来查看优化前的绘制方案
const USE_GOOD_DRAW = true;

// 当边界点数量大于这个值时才会进行挑拣
const PICK_LIMIT = 100;

// 边界点挑选倍率,这个值越大,边界就越“锯齿”,越小就越精细,最小为 0(完整边界)
const PICK_MAGNIFICATION = 25;

// 准备地图
const map = new BMapGL.Map('container');
const bdary = new BMapGL.Boundary()
map.centerAndZoom(new BMapGL.Point(105.95498, 37.99115), 5);
map.enableScrollWheelZoom(true);

// 所有省级行政区
const ALL_PROVINCE = [
    '北京市', '天津市', '河北省', '山西省', '内蒙古自治区', '辽宁省', '吉林省',
    '黑龙江省', '上海市', '江苏省', '浙江省', '安徽省', '福建省', '江西省', '山东省', '河南省', '湖北省',
    '湖南省', '广东省', '广西壮族自治区', '海南省', '重庆市', '四川省', '贵州省', '云南省', '西藏自治区',
    '陕西省', '甘肃省', '青海省', '宁夏回族自治区', '新疆维吾尔自治区', '香港', '澳门', '台湾省'
];

// 获取优化后的边界 bounder
const getOptimizationBounder = bounder => {
    const splittedBounder = bounder.split(';');
    // 如果总数量太少的话就不进行缩减了,不然会很丑(会变成一个三角形或者消失掉)
    if (splittedBounder.length < PICK_LIMIT) return bounder;

    // 等距筛选点位
    const minBounder = splittedBounder.filter((point, index) => !(index % PICK_MAGNIFICATION));
    return minBounder.join(';');
}

// 优化之前的绘制方案(全量绘制)
const badDraw = data => {
    data.boundaries.forEach(bounder => {
        const ply = new BMapGL.Polygon(bounder, {
            strokeWeight: 0.2,
            fillColor: '#ffef7d',
            fillOpacity: 0.8
        })

        map.addOverlay(ply)
    })
}

// 优化之后的绘制方案
const goodDraw = data => {
    const ply = new BMapGL.Polygon(data.boundaries.map(getOptimizationBounder), {
        strokeWeight: 0.2,
        fillColor: '#ffef7d',
        fillOpacity: 0.8
    })

    map.addOverlay(ply)
}

ALL_PROVINCE.forEach(key => bdary.get(key, USE_GOOD_DRAW ? goodDraw : badDraw))
</script>

参考

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 221,198评论 6 514
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 94,334评论 3 398
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 167,643评论 0 360
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,495评论 1 296
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,502评论 6 397
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 52,156评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,743评论 3 421
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,659评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 46,200评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,282评论 3 340
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,424评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 36,107评论 5 349
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,789评论 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,264评论 0 23
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,390评论 1 271
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,798评论 3 376
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,435评论 2 359

推荐阅读更多精彩内容