背景

地图功能是每个系统必备的功能之一,通常可以使用百度地图和高德地图来实现在线地图展示。

但是将展示的地图下载到本地则是一件有挑战的事情。

运行环境

mac系统

java 1.8

其中标注的坐标原始数据是百度坐标,因此代码中进行了转换。

参考代码

需求

某一天领导找我,说客户提出需要将系统里的在线地图下载到本地浏览。利用搜索引擎,找到一些思路,

前端思路: 在线地图是canvas绘制的,可以将canvas转图片存储到本地。

后端思路: 高德和百度均提供静态地图接口,但是存在尺寸限制、标注限制。因此需要程序自己切割下载,拼接地图。

作为一名java后端开发,当然是选择后端解决方案。

这里先说下我的需求:我需要一份地图,是杭州滨江地区的,地图需要标注一些位置。这些位置超过10个。因此无法直接使用高德地图接口下载。

实现过程

以高德地图为例,

首先请注册高德开发者,申请密钥,如此才可以调用接口

public final static String uurl = "http://restapi.amap.com/v3/staticmap?zoom=16&size=1024*1024&scale=1";// 高德地图api

key = "你的密钥";

解释下这个接口的参数。

zoom 地图的缩放比。

size 地图的尺寸

scale 地图清晰度 1表示非高清。2表示高清。如果设定值为2 则 zoom将自动成为17 size将变成 2048

由于下载高清地图,非常慢,以及内存容易溢出,因此这里修改非高清的参数。

直接想要结果的,可以下载一份代码,自己跑一次

这里先解释下思路:

1、经纬度切割。 先确定你需要下载地图的经纬度范围。需要2个点即可。

地图左上角、地图右下角。

例如这里我的坐标是杭州滨江区。

String top = "120.123754,30.232441";
String end = "120.243745,30.141632";

你需要决定将你的地图切割成多少块。

这里我设定步长是

// 按照0.01为步长,循环计算出所需获得的图片中心位置坐标
int countX = (int) ((ex - sx) / 0.010999);// 如果ex - sx = 0,说明只有一列
int countY = (int) ((sy - ey) / 0.00955);// 如果sy - ey = 0,说明只有一行

其中0.010999 表示 横向的地图中心距离 0.00955 表示竖向的地图中心距离。

这2个值 不能随意修改。和缩放比、清晰度存在关联。

2、下载地图

设定了步长后即可运行程序,下载地图碎片。下载有可能失败,请多次执行,直到数目完整。

3、横向切割图片

下载的地图碎片,多数情况存在重叠部分,因此需要切割。切割的规则是 第一张图片保持完整,从第二张图片开始切割重叠的部分,只保留不重叠的图片。因此需要确定起点是关键。

for (int i = 1, j = 0; i <= allPng; i++) {
System.out.println("正在截取");
if (i == (j * count[1] + 1)) {
cutImage(path + "/getMaps/" + i + ".png", path + "/cuttedMaps/" + i + ".png", 0, 0, 1024, 1024);
j++;
} else {
cutImage(path + "/getMaps/" + i + ".png", path + "/cuttedMaps/" + i + ".png", 1024 - 1024, 0, 1024,
1024);
}
}

上图中的 1024-1024 表示计算切割的起点位置。

这个重叠值如何确定,拿 1.png和2.png 对比确定。在打开ps工具手动数数偏差。 如果你重叠的像素是10 则这里应当是 10

这里因为我给的步长十分精确,重叠为0 因此切割的起点位置从0开始。

3、竖向切割

和横向是同理的。需要人工通过ps确定 重叠的像素是多少.

横向是比较第一张和第二张图片,但是竖向则需要 比较 第一列的第一张和 第二列的第一张图片。

System.out.println("开始对图片进行第二次截取...");
File f1 = new File(path + File.separator+"mergedMaps");
String[] mergedImgs = f1.list();
for (int i = 1; i <= mergedImgs.length; i++) {
if (i != mergedImgs.length) {
cutImage(path + File.separator+"mergedMaps"+File.separator + i + ".png", path + "/cutteddMaps/" + i + ".png", 0, 0,
(count[1] - 1) * 1024 + 1024, 1024);
} else {
cutImage(path + File.separator+"mergedMaps"+File.separator + i + ".png", path + "/cutteddMaps/" + i + ".png", 0, 0,
(count[1] - 1) * 1024 + 1024, 1024);
}
}

4、合成

正确设定步长、横竖的切割起点,即可正确拼接出想要的图片了。

下载图片可能不完整,可以重复多次执行,

以下是程序执行的效果。由于是16的缩放比,所以图是很大的。大小是 28MB

image.png

问题

1、拼接的图片,出现错位?

重叠值错误。请重新设定。

2、图片没有重叠?

步长太大,请重新设定。

3、标注不完整,破碎

这个问题非常常见,本人也卡在这里。主要原因是标注的点,正好处于分割线上,然后分割线存在重叠部分,切割的时候导致标注不完整。

这里解决方案主要是,不断改变步长,确保地图水平恰好无缝可以拼接。也就是0重叠,但是线条吻合。传说中的完美分割,本人采用的就是该方案。

另外一个就是 ,位于标注上的点,坐标进行偏移,确保位于重叠的右侧。这样不会被切割。也需要多次人工尝试。

4、高清版本

高清版本的参数和非高清的差异较大。主要是确保是 高清版本的地图碎片尺寸是固定的 2048

而非高清的是 1024 因此需要注意修改参数。

5、切割和合并提示文件不存在

下载的地图不完整,多次运行程序即可。因为高德的该接口似乎很奇怪,经常会失败。