最近的版本有这样一个需求:

有 3 个要素:

  1. 中国地图
  2. 高亮省区
  3. 中心显示数字

面对这样一个需求,该如何实现呢?

高德地图

因为项目是基于高德地图来做的,所以很自然而然的想到了高德。但是当查阅高德地图相关 Api 后,发现并没有能够实现这样需求的方法,所以只能另寻他法了。

图片叠加

让设计师出图,实现第一个要素开发成本极低。至于高亮省区,也是继续让设计师出图,与全国地图分辨率保持一致,为每个省区设计一张高亮的图,其他地方透明,这样算下来设计师得出 35 张图。若不考虑性能,将图片无脑叠加倒也可以实现。但是作为 Android 开发都知道,这样的一张不算小的图片加载到手机里,占用的内存怕是个庞然大物,更别谈极端情况下要叠加 35 张这样的大图了。
优化下叠加方案:将高亮的省区做成小图,一个包含了省区所有区域的矩形,省区内部高亮,其他区域透明,这样图变小了,但是就得计算小图相对于全国大图的相对位置,对于每个小图都得计算一个比例。同时,绘制高亮省区时可以每次都只取2张图进行叠加,叠加完后释放一张图再加载另一张图,而不用一次性全部加载在内存中。这种方案想想是 ok 的,但是感觉依然还是很麻烦。于是继续探索~

SVG Path

其实网上有很多文章也是有类似的需求,简单搜一下就发现了 SVG 这个解决方案了。看了一眼,便决定就是它了!
SVG:可缩放矢量图形(英语:Scalable Vector Graphics,SVG)是一种基于可扩展标记语言(XML),用于描述二维矢量图形的图形格式。元素是 SVG 基本形状中最强大的一个,它不仅能创建其他基本形状,还能创建更多其他形状。

SVG Path 用 Android 绘制

这里先贴一下我找的北京市的 Path 数据:

1
2
3
<svg height="475" width="565">
<path id="Beijing" d="M421.139,189.75L420.782,186.894L419.95,184.989L425.045,182.863L425.426,181.18L424.23699999999997,176.413H422.56899999999996L415.90299999999996,172.964L412.21299999999997,176.654C412.21299999999997,176.654,411.08799999999997,183.239,411.381,181.534C411.66999999999996,179.82999999999998,407.688,185.822,407.688,185.822L407.094,190.108L407.926,192.371L412.807,191.537L416.5,192.608L418.284,190.941L421.139,189.75Z"/>
</svg>

这里要注意一点:SVG Path 里的数据都是在一个固定宽高的矩形里的坐标集合,所以当 Android View 与 SVG 的宽高不一致时,需要进行缩放。注意下面代码中的 scale 属性:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
 * 计算地图边界
* 1.黑龙江是中国最东,最北的省份
* 2.新疆是中国最西的省份
* 3.海南是中国最南的省份
*/
private fun () {
val hljRF = RectF()
xPaths[HEILONGJIANG_CODE]?.computeBounds(hljRF, true) val hnRF = RectF()
xPaths[HAINAN_CODE]?.computeBounds(hnRF, true) mapWidth = hljRF.right
mapHeight = hnRF.bottom
} override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
val speSize = View.MeasureSpec.getSize(widthMeasureSpec)
scale = speSize / mapWidth
setMeasuredDimension(speSize, (speSize * mapHeight / mapWidth).toInt())
} override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
// 缩放画布
canvas.scale(scale, scale)
...
}

再来看到 Path 里有一些 M、L、Z 等字符,这些都是 Path 元素里的指令,后面紧跟的数字即是坐标。

M x,y 移动指令,映射 Path 中的 moveTo
L x,y 画直线指令,映射 Path 中的 lineTo
H x 画水平线指令,映射 Path 中的 lineTo,不过要使用上一个坐标的 y
V y 画垂直线指令,映射 Path 中的 lineTo,不过要使用上一个坐标的 x
C x1,y1,x2,y2,x,y 三次贝塞尔曲线指令,映射 Path 中的 cubicTo
S x2,y2,x,y 跟在 C 指令后面使用,用 C 指令的结束点做控制点,映射 cubicTo
Q x1,y1,x,y 二次贝塞尔曲线指令,映射 quadTo
T x,y 跟在 Q 指令后面使用,使用 Q 的 x,y 做控制点,映射 quadTo
Z path 关闭指令,映射 close

注意小写指令为使用相对坐标,下面 2 行 Path 得到的结果是一样的:

1
2
M421.139,189.75L420.782,186.894
M421.139,189.75l-0.357,-2.856

基于 Android Path 实现不了小写指令的那种效果,所以只能使用大写指令。这里贴一下一个将 SVG Path 转成 Android Path 的工具类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
大专栏  Android 绘制中国地图"line">97
98
99
100
101
102
103
104
105
 * 仅限大写指令转换
*/
public class SvgPathToAndroidPath {
private int svgPathLenght = 0;
private String svgPath = null;
private int mIndex;
private List<Integer> cmdPositions = new ArrayList<>(); * M x,y
* L x,y
* H x
* V y
* C x1,y1,x2,y2,x,y
* Q x1,y1,x,y
* S x2,y2,x,y
* T x,y
* */
public Path parser(String svgPath) {
this.svgPath = svgPath;
svgPathLenght = svgPath.length();
mIndex = 0;
Path lPath = new Path();
lPath.setFillType(Path.FillType.WINDING);
//记录最后一个操作点
PointF lastPoint = new PointF();
findCommand();
for (int i = 0; i < cmdPositions.size(); i++) {
Integer index = cmdPositions.get(i);
switch (svgPath.charAt(index)) {
case 'M': {
String ps[] = findPoints(i);
lastPoint.set(Float.parseFloat(ps[0]), Float.parseFloat(ps[1]));
lPath.moveTo(lastPoint.x, lastPoint.y);
}
break;
case 'L': {
String ps[] = findPoints(i);
lastPoint.set(Float.parseFloat(ps[0]), Float.parseFloat(ps[1]));
lPath.lineTo(lastPoint.x, lastPoint.y);
}
break;
case 'H': {//基于上个坐标在水平方向上划线,因此y轴不变
String ps[] = findPoints(i);
lastPoint.set(Float.parseFloat(ps[0]), lastPoint.y);
lPath.lineTo(lastPoint.x, lastPoint.y);
}
break;
case 'V': {//基于上个坐标在水平方向上划线,因此x轴不变
String ps[] = findPoints(i);
lastPoint.set(lastPoint.x, Float.parseFloat(ps[0]));
lPath.lineTo(lastPoint.x, lastPoint.y);
}
break;
case 'C': {//3次贝塞尔曲线
String ps[] = findPoints(i);
lastPoint.set(Float.parseFloat(ps[4]), Float.parseFloat(ps[5]));
lPath.cubicTo(Float.parseFloat(ps[0]), Float.parseFloat(ps[1]), Float.parseFloat(ps[2]), Float.parseFloat(ps[3]), Float.parseFloat(ps[4]), Float.parseFloat(ps[5]));
}
break;
case 'S': {//一般S会跟在C或是S命令后面使用,用前一个点做起始控制点
String ps[] = findPoints(i);
lPath.cubicTo(lastPoint.x,lastPoint.y,Float.parseFloat(ps[0]), Float.parseFloat(ps[1]), Float.parseFloat(ps[2]), Float.parseFloat(ps[3]));
lastPoint.set(Float.parseFloat(ps[2]), Float.parseFloat(ps[3]));
}
break;
case 'Q': {//二次贝塞尔曲线
String ps[] = findPoints(i);
lastPoint.set(Float.parseFloat(ps[2]), Float.parseFloat(ps[3]));
lPath.quadTo(Float.parseFloat(ps[0]), Float.parseFloat(ps[1]), Float.parseFloat(ps[2]), Float.parseFloat(ps[3]));
}
break;
case 'T': {//T命令会跟在Q后面使用,用Q的结束点做起始点
String ps[] = findPoints(i);
lPath.quadTo(lastPoint.x,lastPoint.y,Float.parseFloat(ps[0]), Float.parseFloat(ps[1]));
lastPoint.set(Float.parseFloat(ps[0]), Float.parseFloat(ps[1]));
}
break;
break;
case 'Z': {//结束
lPath.close();
}
break;
}
}
return lPath;
} private String[] findPoints(int cmdIndexInPosition) {
int cmdIndex = cmdPositions.get(cmdIndexInPosition);
String pointString = svgPath.substring(cmdIndex + 1, cmdPositions.get(cmdIndexInPosition + 1));
return pointString.split(",");
} private void findCommand() {
cmdPositions.clear();
while (mIndex < svgPathLenght) {
char c = svgPath.charAt(mIndex);
if ('A' <= c && c <= 'Z') {
cmdPositions.add(mIndex);
}
++mIndex;
}
}
}

实现

  1. 利用工具类获取每个省区的 Android Path,全部绘制一遍,即可绘制出全国地图(优化:高亮的省区这一步不绘制,避免绘制两次)。
  2. 针对高亮省区,调整画笔颜色再绘制一遍即可。
  3. 显示数量:这个目前没想到什么好方法,只能让设计师参照地图宽高比标出每个中心点的位置,就像这样:

    然后手动算出每个点的横纵坐标占比,再进行绘制。绘制数量计算坐标时仍要考虑 scale 属性。

参考

  1. Android 上绘制中国省份地图
  2. SVG 转 Android Canvas Path

Android 绘制中国地图的更多相关文章

  1. 用matlab绘制中国地图

    reference:https://jingyan.baidu.com/article/870c6fc36fdacfb03ee4be58.html shp: http://muchong.com/ht ...

  2. Javascript实战开发:教你使用raphael.js绘制中国地图

    最近的数据统计项目中要用到中国地图,也就是在地图上动态的显示某个时间段某个省份地区的统计数据,我们不需要flash,仅仅依靠raphael.js以及SVG图像就可以完成地图的交互操作.在本文中,我给大 ...

  3. R绘制中国地图,并展示流行病学数据

    流行病学的数据讲究“三间分布”,即人群分布.时间分布和空间分布.其中的“空间分布”最好是在地图上展示,才比较清楚.R软件集统计分析与高级绘图于大成,是最适合做这项工作了.关于地图的绘制过程,谢益辉.邱 ...

  4. D3.JS V4 绘制中国地图

    参考:http://bl.ocks.org/almccon/fe445f1d6b177fd0946800a48aa59c71 http://blog.csdn.net/lzhlzz/article/d ...

  5. 利用d3.js绘制中国地图

    d3.js是一个比較强的数据可视化js工具. 利用它画了一幅中国地图,例如以下图所看到的: watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvc3ZhcDE=/ ...

  6. 用echarts绘制中国地图

    在项目中运用到图形展示数据太常见了,echarts是一款使用率非常高的插件工具,很多大平台都是使用echarts: 一般运用到条形.折线.扇形图,今天说一说在中国地图上展示各地数据: 首先要准备中国地 ...

  7. echarts 如何在世界地图中绘制中国地图

    1.导入 world.china.js  这个js是将world.js 文件 以及china.js文件进行合并 (网上一些中国地图勾勒的身份曲线感觉很飘  所以自己加工了一下china.js中的数据, ...

  8. 基于D3JS绘制中国地图

    仿照D3JS官网上的美国地图制作了一个中国版的地图. D3JS官网上的版本: http://bl.ocks.org/NPashaP/a74faf20b492ad377312 中国版的地图效果: 如要制 ...

  9. R语言作图 绘制中国地图

    参考:https://zhuanlan.zhihu.com/p/27360411 第一步.下载shapefile文件 一直都没有找到下载地址,死在了第一步 第二步.导入shp文件 第三步.画图

随机推荐

  1. ES6之模块化

    本文介绍ES6实现模块化的方法:使用import和export. 导入的时候需不需要加大括号的判断:1.当用export default people导出时,就用 import people 导入(不 ...

  2. 7.学完linux系统运维到底可以做什么?

    linux运维到底可以做什么?(略有改动原文.排版) 运维,很容易从字面理解为运营.维护. 很多朋友认为,在互联网公司中linux系统运维的工作就是安装系统,部署服务.处理紧急故障,为公司里的开发人员 ...

  3. 吴裕雄--天生自然python学习笔记:python 用pygame模块开发俄罗斯方块游戏

    俄罗斯方块游戏 多年前,游戏机中最流行的游戏就是“俄罗斯方块”了.时至今日,虽然网络 游戏日新月异 ,但“俄罗斯方块”这款小游戏仍在许多人心中 占有一席之地.本例中, 我们将亲手设计一个简单的俄罗斯方 ...

  4. 初识OpenGl

    函数命名规则 OpenGl函数都遵循一个命名约定:<库前缀> <根命令> <可选参数个数> <可选参数类型> 如:glColor3f() ,gl:核心库 ...

  5. 绿洲作业第一周 - 美术Art work

    Dear parents, Please remind your child to learn and finish the work as follows from the art teacher: ...

  6. 让Spring不再难懂-mvc篇

    spring mvc简介与运行原理 Spring的模型-视图-控制器(MVC)框架是围绕一个DispatcherServlet来设计的,这个Servlet会把请求分发给各个处理器,并支持可配置的处理器 ...

  7. openssl 密钥注意

    使用openssl生成的密钥,在对加密字符串进行数字签名的时候,程序一直报错,错误异常: algid parse error, not a sequence​ 其原因是因为,openssl生成的私钥没 ...

  8. [LC] 40. Combination Sum II

    Given a collection of candidate numbers (candidates) and a target number (target), find all unique c ...

  9. By virtue of|sustain|post |scrape off |stretch|access to|take into account of|exploit|hasten|blur |idle|bored her to|account for|accused of|cruelty

    By virtue of this superior quality, this product is often sold out of stockin many areas. 我们的产品因其优秀的 ...

  10. django的orm介绍以及静态文件介绍

    1 django中app的概念 大学:----------------- 项目 信息学院 ----------app01 物理学院-----------app02*强调:创建了app,要在配置文件中注 ...