雷达TIF图上色并覆盖在高德地图上展示
公司项目需求是:将数据库中保存的tif黑白二进制压缩数据展示到地图上面来,实现用户在手机上能实时查看天气情况。数据最终效果与中国气象图很像,但更加方便清晰的让用户在手机端随意缩放查看高清雷电信息。
项目一开始思路导向ArcGIS来解决,但是数据是实时增加的,就需要用程序控制着自动完成。所以我们转向ArcGIS Server,因为专业性太强,网上找不到有用信息,官网文档是英文而且专业术语太强,看不懂。没办法又找到OpenLayers,但是前期的数据黑白tif不能直接覆盖在地图上。
接着在高德找到图片图层,测试后发现网页展示没问题,在手机上性能太Low。最后决定使用高德的XYZ栅格图层解决问题。
经过多次寻找解决方案,最终觉得XYZ栅格可行且手机运行效率高。基本思路是:
1、读取数据库数据,先zip解压数据,然后读取已经解压的tif数据。
2、读取tif图片像素数据,根据像素附带的该点值渲染成RGB相应的颜色。
3、得到一张由tif转成的png图片,将该图切成瓦片(术语:就是有规则的小图片),保存。
4、将瓦片按照一定规则保存起来,前端使用高德栅格图层调用即可。
实现1、2步骤我们从Python转成Golang,因为在Go语言中有处理tif的组件,处理起来比较方便。
实现3步骤时需要了解切瓦片的规则,高德虽然有调取瓦片的接口,却没有怎么切成瓦片的方法。幸好有前人理论和实践铺路: 国内主要地图瓦片坐标系定义及计算原理 (用到平面坐标转瓦片坐标和平面坐标转像素坐标,具体公式也有链接)
切瓦片思路是先计算出图片所在的瓦片,然后新建画布,画布大小就是这些瓦片所组成矩形的大小,将原图放到画布的某个具体位置,然后将该画布切成瓦片即可。(每个瓦片一般256或128像素的正方形)
因为每张tif大小一致,经纬度一致(tif图片自带top、left、right、bottom经纬度),得到图片左上角与右下角经纬度。
参照上面文章中经纬度转瓦片坐标,将两个角转换成瓦片就可以得到图片所在哪些瓦片中,根据所有瓦片计算出像素宽高即可得出画布宽高。再根据原图经纬度转像素坐标,计算出原图在画布的具体位置。(画布增大,原图相应增大,就需要把原图做放大处理)
到4步骤瓦片组成了画布,现在新图已画成,再分成瓦片保存,一般保存规则:原图名称/缩放级别/x_y.png
因为缩放级别一般 3-18左右,按照上面思路计算到10级别,画布就会超级大,严重占内存,即便是我们做测试的服务器16核心32G开启线程65535也只能跑到13级别,且内存cpu满载计算几个小时,再往后直接over。
所以第一条优化方案:地图缩放后,瓦片数量增加,画布增加,原图放大。原图放大到原来尺寸最接近的缩放级别后,下一级别就不再新建画布、放大原图并贴到画布。而是根据缩放规则“上一级别的每个瓦片在下一个级别分成四块”, 思路也是这样。将上个级别生成的瓦片遍历每片都分成四份,每份宽高放大到瓦片宽高,也就是作为新的瓦片。
优化后不在新建更大的画布,内存不会爆,效率有所提升,不过效率依旧有很大提升空间。
第二条优化方案:上一级瓦片放大后有一部分四周边缘瓦片中是没有原图数据的,这部分不需要再分。或者当上个级别瓦片规划分成四份保存成新瓦片时,其中的内容并没有原图内容。
怎么知道没有原图内容呢?还记得经纬度转瓦片坐标吗?对,就是利用它计算该缩放级别下原图所在的瓦片坐标范围,不在该范围的就不需要生成,也就省略后面的放大操作了。
在测试过程中发现图片放大处理是很消耗性能的,因为PNG优化不同于JPG等格式,好在Golang有现成的组件。再就是系统配置可以的话,将进程限制放开也能很好的加速。ulimit -n 65535
写的不够详细,代码量太大也不好展示出来,有这方面需要的道友可以留言告诉我。