前段时间就在考虑如果把我们的IP地址查询网站进行改进,以前把内网的IP地址页面进行了大幅整改,无论是内容、界面都有很大提升,效果还是很明显的,后面还想对外网页面也进行较大的改造。
一个考虑的重点是把图片做成我们网站的特色,让用户可以看到更直观、更有用的图片,准备在图片中显示IP地址的归属地信息,还要加上地图显示,这个地图的显示就很有讲究了,遇到的一个难题是如何把IP地址的归属地经纬度在平面地图上进行标记,而这就需要解决经纬度转平面直角坐标系的问题。
在网上查了一下,国内有帮助的文章不多,而用英文在Google搜索就很快发现一些好的信息,例如:
- Convert from latitude, longitude to x, y 转换经纬度到x, y坐标
- Transverse Mercator projection 横轴墨卡托投影
又陆陆续续找了不少文章查看,基本看明白了几点:
- 这种转换是一个近似转换,不可能是精确解
- 有专门的近似计算公式,可以拿来参考使用
虽然这些年都搞电脑相关工作,但计算中需要涉及三角函数和自然对数这些的地方还是很少,所以看到一些中学时候学的数学公式还觉得有趣:
而推导原理如下图所示:
太深究原理了也没有必要,我就把这公式用好吧,于是在PHP程序中用log, sin等函数来实现了,经纬度在使用sin函数前需要从角度转为弧度再计算,还有一些参数需要反复调整以便合适地展示在地图中。
... //准备经纬度数据,角度转弧度 $longitude_radian = M_PI / 180 * $longitude;//经度,角度转弧度,乘以兀除以180度 $latitude_radian = M_PI / 180 * $latitude;//纬度,角度转弧度,乘以兀除以180度 //准备x,y轴比例数据 $x_scale = $width / 2 / M_PI * 0.97;//这个0.97是因为横行数据核查对比的时候总有一点偏差而乘以的一个估算比例值 $y_scale = $height / 2 / 3;//这个3也是一个经验值,测试了各种大小,3左右误差小,这里是否应该用到 M_PI 未知? //墨卡托投影转换公式 $x = $x_center + $x_scale * $longitude_radian;//x轴坐标,与经度是线性关系的 $y = $y_center - $y_scale * log((1 + sin($latitude_radian)) / (1 - sin($latitude_radian)));//y轴坐标,与纬度是非线性关系的 ...
本来按照公式,$y_scale应该是等于$x_scale的,但我实际用的时候发现相差很大,就各自单独计算设置了。
下面就是我们通过计算将一个IP地址的所在位置经纬度转为平面坐标,再展示在世界地图中的效果:
我找了10多个很小的国家和地区,将IP所在经纬度计算出来的x, y坐标与原始SVG地图中绘图的坐标进行比对,可以实现大约1%左右的精确度,横坐标相对更准,误差在1%以内,纵坐标差一些,而且需要用部分自己试算的参数,有的地方误差有3%左右了。
这个图片还会集成在我们IP站外网页面的综合图片中,为了一幅图花费这么大的力气,我们自己感觉会对用户很有帮助,到时候再写一篇介绍。
评论