17号的时候虫子向我求助,想要我帮忙做一个支持海地的小网站。大体的思路就是,在上面的放一个大地图,然后大家可以留言、上传照片什么的,然后就在地图上出现亮点,然后亮点点进去可以看到留言和照片~这个网站已经进入公测了,大家可以在这里查看:Haiti <3(话说我曾经问虫子“<3”是什么意思,她告诉我,是爱心的意思~)
我猜从来没做过网站的人一定会觉得这个很简单,但我觉得一点都不简单……不过似乎可以实现~这种无疑又是对我技术的一次挑战!
一开始,我想用一种十分取巧的方式——直接用 Google Maps~探究了一段时间的 Google Maps。结果虫子要求:一是地图要暗,二是要出来的是亮点。想想,Google Maps 还是算了呃……(话说其实也不是不可以,不过实现了,很多数据从客户端走,也不大好)。不过像 Google Maps 这样极好的地图图片来源实在是太难得了!于是就直接用了~
首先要攻克的第一个问题是,如果我们知道来访者的经纬度,如何知道把他的点点在地图的什么位置?这是一个很神奇的问题,想想经纬度是为球面设计的,现在要把它转换到一个平面上。说来也巧,原来参与过天文软件 Stellarium 某个版本的翻译工作,正好翻译了其中被称作地图投影的部分。因为有好几个不同的投影方式,我渐渐就明白了,那个就是把球面图形映射到平面的方法!于是就找到了地图投影,经过查询,发现 Google Maps 使用的是 Mercator projection (麦卡托投影法/墨卡托投影法/正轴等角圆柱投影)。找到了投影法的名称,自然转换公式不是难事~不过这个公式看不懂……呃……算了,那些公式显然不在我现在的数学水平能够理解的范围之内,直接用好了……
用前面探究到的 Google Maps 的东西,写了个网页,对比了一下 Google Maps 转换的坐标和我的程序转换的坐标之间的情况,发现基本相等,有一部分会出现 1px 的差异,我就直接忽略之了~在调试这个程序的过程中,比较重要的大概就是 PHP 里面的三角函数相关的函数使用的都是弧度,而我们习惯上使用的经纬度都是角度,这样就需要一点点的转换了……好在 PHP 提供了 deg2rad 这样方便的函数~话说我的地图投影这个程序我把它单独分了一个文件出来,主要是里面定义了各种地图转换需要的常数,如果和别的功能合在一起大约看过去会比较乱……
下面遇到第二个重大的问题是:我们如何知道来访者所在的经纬度?!这个问题似乎比上面那个更复杂了……当然你可以直接请来访者告诉你,不过更好的方法显然是根据 IP 地址识别一下。不过说到 IP 地址的识别精度,那我也无奈了。我猜想在 IPv6 普及前,想要准确根据 IP 地址识别出位置大约是不可能的了。不过,可以接受的精度还是可以实现的~国内的 IP 数据库如纯真这样的,提供的都是一个字符串地址,那个是只能给人看的,电脑可不知道那些东西表示什么……于是就转向了国外的 IP 数据库。果然找到了些有趣的数据库,里面能包含 IP 可能对应的经度和纬度,而且还提供 CSV 下载,可好了~这里可以找到这个好东西:IP address geolocation SQL database :: IPInfoDB。然后我需要一个可以提供 PHP 快速查找的文件。其实这个事情第一次做音乐投票系统的时候,为了限制投票 IP 在福建境内,就做过这个东西。思路也很简单:把每个 IP 段的数据变成定长的二进制数据,然后需要的时候二分查找~于是有了下面的转换用 Python 脚本:
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 | #!/usr/bin/env python # - * - coding: UTF-8 - * - import csv import struct import sys fin = open('ip_group_city.csv', 'r') c = csv.reader(fin, delimiter=';', quotechar='"') fout = open('ipdata.dat', 'wb') c.next() a = 0 s = [] for r in c: a += 1 s.append(struct.pack('>Iff2sh', int(r[0]), float(r[7]), float(r[8]), r[1], int(float(r[10] or '0') * 2))) if a % 10000 == 0: fout.write(''.join(s)) s = [] print >> sys.stderr, a, fout.write(''.join(s)) print >> sys.stderr, a fin.close() fout.close() |
这样,就当获取经纬度差不多了吧~嗯~虽然那个精度实在不敢恭维……不过根据那个网站自己的数据,至少国家是不会弄错的(准确度 90%+),只是城市就不跟你保证了~(那个数据貌似显示,这个数据库中城市在中国只有 71% 左右是正确的……)。
最后是关于地图的最后一个问题……如何绘制?
经过考察,这个小网站未来要放的那个服务器是不支持 ImageMagick 的,于是只能用 GD 库了……
那个小点点,其实就是圆咯。于是我一开始用 imageFilledEllipse 函数在地图上画直径为 3px 的圆。不过那个的效果实在是不大行……另外一件事就是关于上传照片的问题,某些相机比较囧的都把照片方向弄在 Exif 里面……虽然 PHP 有很方便的库可以直接读取 Exif,但旋转和翻转就麻烦了……
关于 GD 库,有一件事我觉得很无语,那就是有好几个比较有趣的函数,如 imageAntiAlias、imageRotate 下面都写着:
Note: This function is only available if PHP is compiled with the bundled version of the GD library.
于是就各种囧的不能在我自己机子上调试……目标服务器的 GD 库倒是符合条件,不过我发现其实想要通用还有一种很好的方法:在 Notes 里面找!通常不能通用的函数在 Notes 里面都有相应的通用解决方案~这次我在 Notes 里面找到了图片翻转、旋转、判断 Exif 方向、平滑绘制圆的函数~我觉得有些时候,下面的 Notes 比上面的文档要有意义的多……平滑绘制圆的函数,我为了半透明又改了一点。当然,改的不完美,也就不敢拿出来献丑了,勉强能用就是了~
此外,还抄来了 WordPress 里面一段创建不同大小图片和创建缩略图的代码,很好用嗯~话说,太久没写 PHP,很多东西都忘的差不多了,比如说输入的数据中有单引号会自动被 PHP escape 掉,这还让我苦恼了一下呃……
另外就是,设计中虫子给我提出了一个神奇的要求:跟 twitter。为了这件事,我进行了各种不和谐的活动,以了解 twitter 的 APIs……然后用了一个带缓冲的转接程序弄好了。
至于客户端部分,我再次使用了 jQuery,jQuery 果然是个好东西。而且为了上传,我又使用了 Uploadify。有赖于上一次做音乐征集系统的经验,这次少走了不少弯路,而且大量的 IE6 的 bug 也被我巧妙地解决了~而且我发现 IE6 的某些莫名其妙的 bug (比如我在制作过程中遇到了一个 div 明明 right 是 0,却超出右边界很多)都可以通过设置 zoom: 1; 来解决……不知道 IE6 这种莫名其妙的浏览器到底是什么样 SB 的团队制作出来的。IE 曾经是带领万维网发展的,现在却成为了严重的阻碍者!用 IE 的孩子们赶快觉醒吧……我真得不想在为了 IE 花掉大把的时间调试了……
顺便说一下新发现的 IE6 的 bug(其实我也不知道是不是我新发现的……):还是隐藏显示的问题。是用 AlphaImageLoader 这个 filter 的元素隐藏后显示,这个 filter 的效果会消失,也就是没用了……为了这个 bug,这次不得不在脚本中都加入了 ie-fix……而且,更严重的是,如果你在显示后再设置这个 filter,又会出现另外一个 bug:这个时候,被设置这个 filter 的元素内部的所有元素可见,但变成不可用,也就是好像点不到一样!我估计是这个 filter 变成了一个在元素内部最上层的一个东西,于是就挡住了其他所有的元素。我汗啊,IE6 的 QA 是干什么吃的啊……这个的解决办法说来也简单,另外弄一个 div,设置在同样的位置,同样的长宽,对他用那个 filter 就行了……
对了对了!说到 AlphaImageLoader 这个 filter,我又想起来一件事,证明了百度不适合用来搜索技术资料,至少比 Google 逊太多了:你分别在这两个搜索引擎里面输入“filter:progid:DXImageTransform.Microsoft.AlphaImageLoader”,我把结果贴出来:
请认真对比我的关键词和两个引擎的结果,你会发现:百度第一页没一个是我要找的,甚至连一个完整匹配的关键词都没有!难道是我的关键词是我瞎编的?不,你看 Google 的结果吧……嗯……事实胜于雄辩,百度众自重……
整个网站的代码,可能在经过完善以后会公布,也可能不公布。由于这次的东西不是做给自己的,所以不能完全我自己说的算了……
参考资料:


好厉害呀…
谢谢…
这个。。。。毕竟是国外的IP库。。我用电信的ADSL,是动态IP,经常会被直接认成是北京的。。。
如果打算用这个IP库来做投票系统的判断的话,恐怕要出大问题。。。至少我要断线、拨号重试N次才能换成一个被正确识别的福建IP。。。
我当年投票判断使用的是纯真库,查找关键词福建……