2012年12月21日星期五

ArcGIS中读取.bil文件

客户现有一应用程序可读取.bil栅格文件作为高程背景显示,如下图:
planetbil 
数据文件有:demo.bil文件(2,077,616字节),index文件(74字节),projection文件(58字节)。现需要转入ArcGIS平台来进行显示,查看帮助,ArcGIS对.bip栅格格式有原生支持。但在ArcMap中加载该文件时,确报错无法打开。
查看ArcGIS关于.bil格式的帮助说明,得知.bil是一种未经过压缩的二进制文件。存储格式如下图:
bilformat 是按行依次存储每个波段每列像元信息的。要在ArcGIS中显示,必须有一个.hdr头文件描述其所需的一些元数据信息才行。而拿到的数据中并没有这个.hdr文件,所以要构造出该文件。查看.hdr文件的说明,是以文本方式记录了该栅格文件的行、列数,每个像元值所占的大小等内容。而原来的应用程序之所以能够读取该图像,也离不开这些信息。用记事本打开index文件,内容如下:

demo.bil 641890.000000 656130.000000 3251870.000000 3281050.000000 20.000000

乍一看并没有.hdr文件中所需的信息,但能看出上述的5个数字分别代表了.bil文件空间范围和空间分辨率。由此我们来推导.hdr文件所需的元数据信息:

  • NROWS:栅格文件的行数,(3281050-3251870)/20=1459;
  • NCOLS:栅格文件的列数,(656130-641890)/20=712;
  • NBANDS和NBITS:分别代表栅格文件的波段数和存储一个波段中每个像元值所需的比特数(pixel depth),index文件中并没有这两个信息。但.bil文件是未经过压缩的,所以我们用.bil文件的大小除以行数再除以列数,2077616/1459/712=2,得知每个像元位置上所有波段值共占2个字节(16bit)空间。理论上可以是NBANDS等于1,NBITS等于2×8=16,也可以是NBANDS等于2,NBITS=8。但我们知道逻辑上此图记录的是高程信息,相当于dem,所以应该只有一个波段,由此得知NBANDS=1,NBITS=16;
  • PIXELTYPE:由于是高程信息,所以pixel value都是正值,因此此处是UNSIGNEDINT;
  • BYTEORDER:存储每个像元值字节的排列顺序。可以由高位到低位的M,也可以是由低位到高位的I。结合原程序代码,得知此处是M。

由此创建的.hdr文件如下:
image  有了正确的.hdr文件后,就可以在ArcMap中直接加载了(projection文件用于设置正确的投影信息):
image

感谢@红毛小屁孩的指导帮助,比帮助文件好用得多。

2012年12月10日星期一

Portable Basemap Server发布2.0.7版本

更新内容:
~为影像数据源增加本地缓存(.cache文件)功能。详见这里
~在线地图转换/下载到MBTiles格式时,增加按Shapefile文件范围下载功能。详见这里
~为转换后的MBTiles文件增加了”压缩”选项,某些情况下可大幅减小MBTiles文件体积。详见这里
下载地址:https://geopbs.codeplex.com/
详见:http://blog.newnaw.com/?p=890

2012年12月3日星期一

出租车拼车应用

和上回的地图微博一样,这只是个想法,欢迎各位转载(请注明出处),讨论,实现。
拼车应用搜索一下已经有不少,可问题是普遍功能过于繁琐,需要注册,并且拼车需要事先商定,不够自由和实用;前些天看了有报道说重庆推出了打车的移动应用,用户可以呼叫附近的空车。可问题是,在大多数人需要打车到时候不是看不到附近的出租车,而是根本就没有空车。这个与用手机查询附近厕所在哪,加油站在哪一样,有点多此一举,直接动嘴问问要快速环保得多。
所以这次设想的是一个专门针对出租车的拼车移动应用。虽然一些城市已经开始鼓励拼车,但实施起来问题很多,经常造成矛盾,设想有了这个应用后,出租车拼车可能会变得方便可行受欢迎。应用分两种客户端,一个是乘客用的,一个是出租车司机用的。

  1. 如何拼车:乘客打开应用后,可自动获取自己当前所在位置,只需输入自己想去的目的地即可;出租车司机在每拉到一个客人后,输入一下本次送客的目的地即可。这样出租车的客户端就可以计算出从当前位置到达目的地的所有路线,并显示出所有可行路线附近,与本次目的地相同的想要搭车的乘客位置,司机就能根据意愿,准确找到可以捎带的乘客,而不用盲目停车询问;乘客输入目的地后,可在手机上看到,行进路线经过当前或附近位置,并且目的地与自己相同或相近的附近出租车,通过点击该出租车,就可以向出租车司机发出拼车请求,出租车司机收到请求后(可能有多个拼车请求),可以根据意愿选择要捎带的乘客。在没有拼车成功之前,出租车的客户端可根据当前的行进路线,动态刷新周围需要拼车乘客的信息;而乘客也可实时看到,可以满足自己拼车要求的出租车位置;乘客和出租车都有发起请求或接受请求的机会;
  2. 如何避免抢客或抢车冲突:仅仅有匹配机制还不行,因为如果多辆出租车同时想拉同一个乘客或者多个乘客想拼同一辆车就会产生冲突,这时可以通过和阿凡达星球的生物配对一样的机制,来解决这个问题。当乘客向出租车发起请求,该出租车应答了乘客请求后,或出租车向乘客发起请求,乘客应答了出租车后,即认为他们配对成功,就会同时从其他所有乘客和出租车的客户端上消失;对于出租车而言,本来想去拉乘客A,但还未走到跟前发现A已经消失了,就知道A被同行拉走了,应用会自动推荐并显示下几个适合捎带的乘客,由于没有绕路和停车,就不会影响最先上车的乘客;对于乘客来说,本来想搭乘出租车A,但还没发起请求或发起请求未收到应答之前,就发现A已经消失了,就知道A拉了别的乘客或者出租车拼的已经满掉了,即可向下一辆车发出请求,这对于打车的人来说再正常不过;
  3. 如何避免恶意请求或恶意应答:这个可以借助评价体系和移动设备的定位功能来解决。乘客和出租车都有相应的信誉值,比如淘宝的几颗心或者几颗钻。乘客发起或应答请求时自然会选择钻比较多的出租车,出租车应答或发起请求时自然会选择心比较多的乘客。信誉值如何累计呢?如上面所说,乘客和出租车建立阿凡达连接双双消失在地图上后,他们必然会为本次建立连接而增加或减少信誉值。依据就是乘客的位置和出租车的位置是否有过重叠。如果是乘客发出的请求,系统会以乘客当前位置为评价标准,如果出租车到过该位置而最终此次拼车没有成功,则可能是乘客放了鸽子,搭别的车走了,乘客信誉减少;如果是出租车发起的请求,系统仍然以需要搭车乘客的当前位置为标准,如果此次拼车未能成功是因为出租车根本没有经过该位置,则可能是出租车放了乘客的鸽子,出租车信誉减少;拼车成功并结束后,双方信誉加一;
  4. 如何计算合理的收费:有了位置信息后这个就很简单了,按照乘客上下车经过的路线来计程即可,避免了传统拼车司机不好合理收费的问题。而收费时可以利用移动设备NFC(近场通信)功能来完成,只需将手机和出租车客户端接近一次即可完成收费付费过程,具体应用可以是支付宝/paypal或者网银/google wallet。

一些补充:

  • 出租车使用的客户端可以是具有语音输入输出,大字体显示功能的平板设备,方便司机在不停车的情况下进行操作(这在国外已经是事实了);
  • 这个应用双方无需注册即可使用,减少部分用户对于任何注册过程的抵触情绪。但如果不注册则无法显示用户信誉,减少拼车成功的可能性(对于低信誉的用户如果想要通过匿名使用的方式来隐藏自己的劣迹的话,若干次后服务器可将其列入黑名单或标记该移动设备为欺诈等级,依据是设备的mac地址或其他唯一标识);
  • 对于平台运营方来说,功能简单所以实现成本小,应用必须通过服务器端交互所以具有很强的用户粘黏度,不用担心用户流失或被抄袭;
  • 可抽成,在每一次拼车成功支付时抽成5毛,做为平台报酬;
  • 如此拼车若可行,可考虑适当降低燃油附加费甚至起步价,降低乘客成本的同时也可给司机带来更多的收入机会;
  • 至于拼车是否合法的问题在此不做讨论。

2012年12月1日星期六

此刻的心情

两种极端的情感同时袭来,两种不知所措的感觉,让我双倍不安,不知如何是好。

2012年11月29日星期四

Portal for ArcGIS迁移

针对目前的1.6.2.1版本。
Portal for ArcGIS产品的目录结构如下:

portal
--mysql-5.5.9-winx64
--…
arcgisportal
--content
--db
--index
--logs
--portal-config.properties

portal文件夹下存储Portal for ArcGIS所要用到的若干应用,包括对账户,组,权限等关系进行存储的mysql程序;arcgisportal文件夹下存储相关的数据文件,content里存储所有用户上传的item信息,db目录即是mysql内数据库文件的物理存储路径,index和logs文件内容如其名。portal-config.properties文件存储了Portal for ArcGIS产品的配置信息。
如果部署了Portal产品,并在其中建立了账户,组,上传了项目,这时想要将这些内容迁移到另一台机器的Portal中去,只需要停止Portal相应的服务,拷贝arcgisportal文件夹下的content,db,index三个文件夹并覆盖新Portal中的同名目录即可。如果不想停止Portal服务的话只要用mysql的工具备份/还原其中的geowarehouse这一个数据库即可。
ps:portal\mysql-5.5.9-winx64\data目录下的文件夹可以忽略或删除,因为从my.ini文件可以看到物理存储位置是在arcgisportal\db文件夹下;
pps:Portal for ArcGIS产品用于搭建GIS资源门户网站,与云环境配合可为企事业单位节约大量成本。目前安装非常简单,只需install和portal start两个命令即可,10秒之内搞定。具体功能可参考ArcGIS.comGISDay

2012年11月15日星期四

Portable Basemap Server发布2.0.6版本

更新内容:
~Portable Basemap Server已在CodePlex上开源,LGPL协议;
~添加ArcGIS Cache/在线地图数据源到MBTiles格式(.mbtiles)的转换/下载工具。详见这里
~为在线地图数据源添加本地缓存功能。详见这里
~改进ArcGIS Tile Package(.tpk)格式文件的读取速度;
~修复已知bug;
详见:http://blog.newnaw.com/?p=890

2012年10月8日星期一

Stan

  听Eminem大概是从上大二开始,《8 mile》也很喜欢,Stan这首歌自然凭借它的旋律给我留下了深刻的印象。以前只知道Dido部分的旋律很好听,但最近才仔细看了这首歌的歌词,才知道里面有这么悲伤、励志的一个故事,像看了一场电影。

移动设备观看
Chorus: Dido
My tea's gone cold I'm wondering why I..
茶越来越凉,我始终不知道为什么…
got out of bad at all
我的心情总是不好。
The morning rain clouds up my window..
清晨的雨遮住了我的窗
and I can't see at all
我什么也看不见了
And even if I could it'll all be gray,
看见的都是灰色
but your picture on my wall
但在墙上却有你的照片,
It reminds me, that it's not so bad,
它提醒我,这还不算坏,
it's not so bad..
还不算坏……
1st Chorus: volume gradually grows over raindrop background
2nd Chorus: full volume with beat right after "thunder" noise
Dear Slim, I wrote you but you still ain't callin
亲爱的Slim,我写信给你,但你仍未答复。
I left my cell, my pager, and my home phone at the bottom
我把我的地址、姓名和电话都留在了信末,
I sent two letters back in autumn, you must not-a got 'em
秋天里发了两封信,你肯定都没有收到
There probably was a problem at the post office or something
可能是邮局或是什么地方搞错了
Sometimes I scribble addresses too sloppy when I jot 'em
有时我总是把地址写得很潦草
but anyways; fuck it, what's been up? Man how's your daughter?
但又能怎样呢?操他的蛋吧!你的女儿好吗?
My girlfriend's pregnant too, I'm bout to be a father
我的女朋友也怀孕了,我就要做爸爸了
If I have a daughter, guess what I'ma call her?
如果是女孩,你猜我会叫她什么?
I'ma name her Bonnie
我会给她起名叫Bonnie
I read about your Uncle Ronnie too I'm sorry
我也读到了你的Ronnie叔叔的事儿,我很难过。
I had a friend kill himself over some bitch who didn't want him
我有个朋友因为一个女人不要他而自杀了,
I know you probably hear this everyday, but I'm your biggest fan
我知道你也许每天都听到这个,可我是你的超级歌迷
I even got the underground shit that you did with Skam
我甚至有你和Skam一起做的地下音乐
I got a room full of your posters and your pictures man
我的房间里贴满了你的海报和照片
I like the shit you did with Rawkus too, that shit was phat 我也喜欢你和Rawkus的歌,那棒极了
Anyways, I hope you get this man, hit me back,
反正,我很希望你能看见这封信,给我回信吧,
just to chat, truly yours, your biggest fan,This is Stan
只是聊个天,我是你最忠诚的歌迷―stan
{Chorus: Dido}
Dear Slim, you still ain't called or wrote, I hope you have a chance
亲爱的Slim,你还是没有给我回复,希望你能有机会回信
I ain't mad - I just think it's FUCKED UP you don't answer fans
我还没疯,我只是认为你不给歌迷回复简直太糟糕了
If you didn't wanna talk to me outside your concert, you didn't have to,
如果你不想在剧场外理我,你就可以不理,
but you coulda signed an autograph for Matthew
但至少应该为Matthew签个名。
That's my little brother man, he's only six years old
那是我的小弟弟,只有六岁
We waited in the blistering cold for you,four hours and you just said, "No."
我们在该死的寒风中等了四个小时,可你只是说了个“不”
That's pretty shitty man - you're like his fuckin idol
这太TMD糟糕了,伙计,那个小家伙可是把你当成了他的偶像。
He wants to be just like you man, he likes you more than I do
他想要成为像你那样的人,他比我更喜欢你。
I ain't that mad though, I just don't like bein lied to
我还没疯,我只是不喜欢被欺骗。
Remember when we met in Denver - you said if I'd write you
记得我们在丹佛的相遇吗?你说如果我写信,
you would write back
你会回信的
see I'm just like you in a way
看吧!我们其实有相同的境遇
I never knew my father neither;
我也不知道我爸是谁,
he used to always cheat on my mom and beat her
他总是背着我妈妈在外面鬼混而且还打我妈妈。
I can relate to what you're saying in your songs so when I have a shitty day, I drift away and put 'em on, cause I don't really got shit else so that shit helps when I'm depressed
我能在你的歌里看到自己。所以在那些心情沮丧的日子里, 我就飚着车漂着移听着你的歌,
因为只有你的歌能帮我。
I even got a tattoo of your name across the chest
我甚至在胸前纹上你的名字
Sometimes I even cut myself to see how much it bleeds
有时我也会割开手腕看它到底能流多少血
It's like adrenaline, the pain is such a sudden rush for me
那样非常刺激,那些痛楚一阵阵向我袭来
See everything you say is real, and I respect you cause you tell it
看,你说的什么事情我都认为是对的,因为那是你说的
My girlfriend's jealous cause I talk about you 24/7
我女友开始嫉妒,因为我每天都在谈论你
But she don't know you like I know you Slim, no one does
她不像我如此的了解你,没人能
She don't know what it was like for people like us growin up
她不会明白有着我们这样成长经历的人
You gotta call me man, I'll be the biggest fan you'll ever lose
你一定要回信,我是你永远都不能失去的最忠实的歌迷
Sincerely yours, Stan .
你真诚的朋友,Stan
-- PS: We should be together too
另外,我们也将会在一起的
{Chorus: Dido}
Dear Mister-I'm-Too-Good-To-Call-Or-Write-My-Fans,
亲爱的从不回信先生
this'll be the last package I ever send your ass
这将是我寄给你最后的邮件了
It's been six months and still no word - I don't deserve it?
六个月了,你一个字都没回―我不配得到你的回信吗?
I know you got my last two letters;
我知道你收到了最近的两封信;
I wrote the addresses on 'em perfect
我把地址写的清清楚楚
So this is my cassette I'm sending you, I hope you hear it
这回是我要寄给你的磁带,我希望你能听到
I'm in the car right now, I'm doing 90 on the freeway
我在车上,在高速路上开到90
Hey Slim, I drank a fifth of vodka, you dare me to drive?
嘿,slim,我喝了15瓶伏特加,你说我还能开吗?
You know the song by Phil Collins, "in the air tonight"
你知道PhilCollins的那首歌“in the air tonight”吗?
about that guy who coulda saved that other guy from drowning
那首歌说的是有一个人可以救另一个落水的人,
but didn't, then Phil saw it all, then at a a show he found him?That's kinda how this is, you coulda rescued me from drowning
但那家伙没有去救,Phil看到是事件的全过程,并且在后来的一个演出上找出了那个见死不救的人,那情形就和现在一样,你本可以阻止我溺水,
Now it's too late - I'm on a 1000 downers now, I'm drowsy
但现在一切都晚了,我吃了1000片安眠药
and all I wanted was a lousy letter or a call
眼睛睁不开了,我想要的只不过是个该死的回信
I hope you know I ripped +ALL+ of your pictures off the wall
我希望您能知道我把你所有的照片都从墙上撕了下来
I love you Slim, we could be together, think about it
我爱你slim。我们本应该在一起的,想想吧
You ruined it now, I hope you can't sleep and you dream about it
现在你把一切都毁了,我希望你会因此而失眠,你一定会梦到这一切。
And when you dream I hope you can't sleep and you SCREAM about it
当你梦到时,你会醒来并且尖叫
I hope your conscience EATS AT YOU and you can't BREATHE without me
我希望你会受到良心的谴责,失去我,你将无法呼吸
See Slim; {*screaming*} Shut up bitch! I'm tryin to talk!
看看吧,slim贱货,闭嘴!我正在说话呢
Hey Slim, that's my girlfriend screamin in the trunk
嘿,slim,那是我的女友在后备箱中尖叫
but I didn't slit her throat, I just tied her up, see I ain't like you
但我不会割开她的喉咙,我只是绑住她,看,我才不像你呢
cause if she suffocates she'll suffer more, and then she'll die too
因为窒息而死会让她更痛苦,而且她马上也就要死了
Well, gotta go, I'm almost at the bridge now
好了,该走了,我已经到桥上了,
Oh shit, I forgot, how'm I supposed to send this shit out?
噢,见鬼,我TMD怎么才能把磁带寄出去呢?
{*car tires squeal*} {*CRASH*}
.. {*brief silence*} .. {*LOUD splash*}
{Chorus: Dido}
Dear Stan, I meant to write you sooner but I just been busy
亲爱的Stan,我本打算尽快回信给你,但最近太忙了
You said your girlfriend's pregnant now, how far along is she?
你说你的女朋友怀孕了,她现在怎样了?
Look, I'm really flattered you would call your daughter that
你能给你女儿起那个名字,我真的感到十分高兴
and here's an autograph for your brother,
这里有给你弟弟要的签名,
I wrote it on the Starter cap
我写在了照片的上面
I'm sorry I didn't see you at the show, I must miss you
很抱歉在演唱会上没能见到你,我肯定是错过了
Don't think I did that shit intentionally just to diss you
不要认为我写那些歌是故意让你难过的
But what's this shit you said about you like to cut your wrists too?
但是你说你自残割自己手腕,那是多么愚蠢的事情啊?
I say that shit just clownin dogg,
我唱那些只是说着玩的,笨蛋
c'mon - how fucked up is you?
拜托,你TMD在搞什么?
You got some issues Stan, I think you need some counseling to help your ass from bouncing off the walls when you get down some
你有些小问题,Stan,我想你需要些建议来帮帮你如何在沮丧的时候坚强起来
And what's this shit about us meant to be together?
你说我们将会在一起是什么意思?
That type of shit'll make me not want us to meet each other
这种话让我不想和你见面
I really think you and your girlfriend need each other
我想你和你的女友确实是相互需要的
or maybe you just need to treat her better
而且你应该对她好一些
I hope you get to read this letter, I just hope it reaches you in time, before you hurt yourself,
希望你能读到这封信,但愿它能赶在你伤害自己前寄到,
I think that you'll be doin just fine
我感觉那会让你好起来的
if you relax a little, I'm glad I inspire you Stan, but why are you so mad? Try to understand, that I do want you as a fan
如果你放松一下,很高兴我可以激励你,但你为何那样疯狂,要试着去理解,我非常喜欢你当我的歌迷
I just don't want you to do some crazy shit
我不想再看到你做疯狂的举动了
I seen this one shit on the news a couple weeks ago that made me sick
几周前我看到一个让我恶心的新闻
Some dude was drunk and drove his car over a bridge
一个家伙喝醉了并且开车冲到了桥下
and had his girlfriend in the trunk, and she was pregnant with his kid
后备箱里是他怀孕的女友
and in the car they found a tape, but they didn't say who it was to
在车上还找到了一盒磁带 可他们没说磁带是给谁的……
Come to think about it, his name was.. it was you
我想想,他的名字是……是你
Damn!
该死的!

2012年9月17日星期一

没想到

  钓鱼岛自古以来就是中国的固有领土。
======================分割线========================
  没想到这么久后才写了一篇非技术的文章;没想到一写就是这么敏感的话题。上面那一段是我的个人立场。
  这两天因为别的原因都要出门,恰巧看到了一部分因为日本政府的言行所引起的反日游行。我一直觉得任何人或事,在不了解真相的时候就不能妄下评论,所以就游行这件事本身我没有什么好说的,因为我并不知道我所看到的东西的来龙去脉。我只想告诉大家我的一点感受,这些也许和你们坐在电脑前转发评论微博的感受不太一样。
  我站在十字路口的西南侧,游行队伍在十字路口的正北侧(相距大约100米左右),十字路口中心区域为真空地带。游行队伍骚动时西侧的民警就会要求大家往后退,我确实想起了《盗梦空间》里一层梦境中齐藤被按在二楼小房间地上时,窗外的场景,而我所看到的只不过是游行队伍里扔出来的几个矿泉水瓶子而已。你也许觉得夸张了,你也许觉得是我胆小了,我只是陈述一下我的感受。
  没想到的还有一件事情,我从事的技术行业内有人开始扯政治了。震惊:美企在中国地图上直接抹杀钓鱼岛和藏南!【转自铁血网精华帖】,我读完了整个日志和帖子,觉得作者的文笔和气势非常好。
  但这件事情我是了解始末的,所以我觉得自己可以发表一点看法。公司为了生存,都会有本地的公关办法。比如google地图中国版写的是“钓鱼岛”google地图日本版写的是“钓鱼岛 尖阁诸岛”。Esri同样有本地化的地图,上面写的同样是“钓鱼岛”。barry说的对,地图只代表一个国家对世界的认知而已。我14岁的时候看到外国地图上少印一个钓鱼岛也会生气,也会震惊;但现在觉得就算地图上印的是钓鱼岛,也不等于现在上岛就不会有小日本干扰你。只有让自己真正强大起来,才能从根本上改变别人对你的看法,用手去捂住自己或同胞的眼睛,只会给别人留下笑柄。王小波说过,人一切的痛苦,本质上都是出于对自己无能的愤怒。写这个文章的朋友,用不着动气。
  最后看到上面博客中提到的铁血网精华的帖子,发贴日期是2012-9-16,作者id是“天地日月在我心”,注册日期也是2012-9-16(昨天),希望此人不是真正的铁血论坛网友,否则我以后绝对不会再看任何出自这个鸡血论坛的文章了,虽然我以前也没有看过。
======================分割线========================
  人类为什么为了一个小岛如此动怒?你只是这个星球上众多物种的一千万分之一,你和其它一千万分之九百九十九万九千九百九十九商量过你要占领岛屿这个想法么?

2012年9月2日星期日

WebGL Globe实例

  WebGL Globe是Google Data Arts Team建立的基于WebGL技术的地理数据可视化工具,最初用来向人们展示全球用户使用Google搜索引擎的概况
点击打开地图  WebGL Globe开源代码在这里,我做了两个小例子。一个是国内(台湾省数据暂缺)及美国每个县(郡)的人口数据,由于数据来源,统计方法不同,例子中两者间的绝对值没有可比性,可比性仅限于各自国家之内。美国人口最多的地方是Los Angeles,国内人口最多的地方是广东普宁及安徽临泉。可看出国内西部地区每个县面积较大,但人口相对少。国内数据来源于同学写论文时千辛万苦的收集(据说需要保密),美国数据是Esri自带的。
image
  另一个例子是世界陆地上各处的高程,数据来源于Esri免费提供的World Elevation Services。   
 点击打开地图    建议在Chrome或IE10浏览器下观看。

Always backup first…

  今天看到个Wordpress的插件BackWPup,据说是备份wp的好帮手。突然想起自己博客上次备份已经不知道是什么时候了,觉得有点对不住它,就想用这个工具试一下。
  在备份前我就想知道如何在将来用备份的文件进行恢复,于是就在BackWPup选项中找到了restore,当时眼一花,手一滑,点了restore,在那十分之一秒内想,总不会这样就直接恢复了吧?怎么也应该有个确认提示吧?结果直接看到“恢复成功,现在可以删掉备份的xxx文件”,擦。。。回首页一看,10年5月,time machine!。。。
  原来wp的根目录下竟然有10年5月的一个mysql备份文件;BackWPup竟然就需要这么一个文件在这么一个地点;BackWPup恢复时竟然就没有确认选项。。。
  最后用胡辣汤期货为诱饵,让鲶总找到了今年3月他不经意的一次mysql备份,恢复;又用BlogSync和live writer从另一个平行世界同步回了剩下的帖子。赶快在BackWPup设置了一个任务:每天夜里3点自动把博客统统打包,发送到dropbox里去。。。

2012年8月16日星期四

利用Nginx做反向代理搭建ArcGIS 10.1 for Server集群环境

  搭建GIS Server集群环境时,通常不建议在GIS Server之间设置防火墙;而建议在服务器环境的前端设置反向代理来隐藏服务器环境的真实地址及端口,保险起见可将反向代理放入DMZ区(前后都设置防火墙),增加安全性。
Multiple firewall scenario with reverse proxy web server
  ArcGIS 10.1 for Server做出的架构改进使得我们在搭建GIS服务器集群环境时更加容易和省心;Nginx因其高性能,耗资源少,稳定性高,成本低廉,配置简单等诸多特性被越来越多地使用。这里简单介绍如何利用Nginx做反向代理并实现Web层的负载均衡效果,来搭建ArcGIS 10.1 for Server集群环境的过程。具体环境如下:
image
  • 192.168.0.247(Windows Server 2008 R2):安装Web Server(IIS 7.5),Web Adaptor(使用默认80端口),ArcGIS 10.1 for Server(使用默认6080端口);
  • 192.168.0.244(Windows Server 2003 R2):安装Web Server(IIS 6.0),Web Adaptor(使用默认80端口),ArcGIS 10.1 for Server(使用默认6080端口);
  • 192.168.0.69(Windows 7):运行Nginx,监听本地80端口;
其中192.168.0.247和192.168.0.244两台机器安装ArcGIS 10.1 for Server,加入同一个site,组成GIS Server集群,并分别安装Web Server和Web Adaptor,组成Web Server集群;192.168.0.69机器上运行Nginx,监听80端口,充当反向代理服务器,并将接受到的请求负载均衡到247和244两台机器上。这样既利用了企业级Web Server的性能,也一定程度上保证了内网环境的安全。Nginx配置过程如下。
  由于是Windows环境,从官网上下载稳定的1.2.3版本,解压到192.168.0.69机器的c:\nginx-1.2.3目录下。因为我们打算让Nginx监听80端口,所以需要将本身占用80端口的IIS默认网站停掉。打开conf目录下的nginx.conf文件,修改如下:
image  命令行方式输入'start nginx'启动Nginx,然后发送请求到192.168.0.69机器的80端口:
image  看到请求被转发到了247机器的IIS 7.5上,再次刷新,
image  请求已经轮询到了244机器的IIS 6.0上。最后通过69机器来访问ArcGIS Server的Services Directory,
image  此时Nginx会以轮询的方式访问247和244两台机器的Web Adaptor。
  这里只是针对ArcGIS for Server集群环境配置做了简单介绍,在实际中可能还需考虑搭建可容错的共享存储环境,DNS服务器的配置,硬件防火墙的配置,Nginx具体参数的配置等因素。

2012年8月14日星期二

ArcGIS 10.1 for Server机器判断本机是否已经加入site的依据

  在一台机器上安装ArcGIS 10.1 for Server后,它还无法立即工作,必须将其加入site(的cluster)之中才行。首次登录manager时,也会询问创建新的site还是加入已有的site;如果已经加入site,登录manager则会显示该site的管理目录。那么机器自身是如何判断出自己是否已经加入site了呢?
  依据就在于Program Files\ArcGIS\Server\framework\etc目录下的两个xml文件,config-store-connection.xml和machine-config.xml。加入site时会在本机创建这两个文件,退出site时会删除之。前者记录site的公用信息,后者记录本机在site中的信息,具体内容大家可以自己查看。
  登录manager时会读取这两个文件,如果找不到,则认为自己没有加入site,会询问创建新site还是加入已有site;如果有这两个文件,会首先访问site的config-store目录验证其内容,合法的情况下就打开site管理目录。 如果在一台机器关机时,从site中将其删除,开机后会根据config-store-connection.xml内容去site的config-store目录中验证,发现自己已经被从site中移除后,会自己删除本机的这两个xml文件。
  此外,如果你的机器已经加入了site A,但现在需要临时加入site B去做一些工作,就不用在admin api去删除site A了,只需将上述两个xml文件先转移到别的地方,然后登录manager,选择加入site B即可;之前site A的config-store内容可以保留,以便将先前挪走的两个xml文件复原后(需要重启ArcGIS Server服务),登录manager即可直接返回site A。

2012年8月10日星期五

ArcGIS 10.1 for Server中Web Adaptor的工作原理

  ArcGIS 10.1 for Server的安装目录中,都会内置一个tomcat(Program Files\ArcGIS\Server\framework\runtime\tomcat\),无需单独的Web Server即可发布各种GIS服务(对于熟悉.NET的朋友来说,更省去单独安装和配置IIS的步骤);AGS应用程序的端口号也由之前版本的80(ArcGIS Server for .NET)和8099/8399(ArcGIS Server for Java)统一到了6080上,比如http://localhost:6080/arcgis/rest/serviceshttp://localhost:6080/arcgis/manager等。但对于内置的这个tomcat,我们并不能进行过多的操作,如果需要部署Web应用或通过别的端口来访问ArcGIS for Server的服务,这时就需要用到新的组件:Web Adaptor了。
  Web Adaptor实际上是安装在Web Server机器上的一个Web应用程序,负责将Web Server接收到的GIS请求转发到ArcGIS Server site内的GIS Server机器上去。通过Adaptor这个桥梁,我们就能使ArcGIS for Server利用到企业级Web Server的诸多优势。根据安装帮助中的解释,安装Web Adaptor后主要有以下好处:
  • 将单独的企业级Web Server集成到ArcGIS for Server的部署架构中来。可以将利用ArcGIS for Server服务的Web应用部署到Web Server上去;可以为site内所有的GIS Server提供不含有6080端口及arcgis等字样的统一访问入口点;
  • 可阻止最终用户通过Web Adaptor的访问地址连接到ArcGIS for Server的相关管理程序。比如用户如果得知了类似6080端口的GIS服务访问地址,就可通过固定的url地址访问到Manger,Admin api等涉及到整个站点操作的内置应用。而Web Adaptor给我们提供了选项,可禁止用户通过Web Adaptor暴露出的GIS服务地址访问到这些管理应用。
此外,相对于site内的多台GIS Server机器,Web Adaptor还可以再次(除了Web Server集群外)起到负载均衡的作用,这在大并发量的情况下会比较有效。假设一个site内有n台GIS Server,它们所能处理的最大并发请求是1w个;根据ArcGIS for Server中p2p的实时通信架构,当我们将1w个并发请求发送到任一GIS Server的tomcat上时,GIS Server之间会自动以轮询(默认设置)的方式处理这些请求,GIS Server们完全可以及时响应;但1w个Web请求会首先使接收请求的这个tomcat过载,产生瓶颈。如果有了Web Adaptor,当这1w个请求到达Web Server(集群)时,Adaptor会将这些请求轮询发送到每个可用的GIS Server去,这样每个tomcat就只需支持1w/n个并发即可,可最大限度利用硬件资源。
  以IIS中的Web Adaptor为例(经过Java高手验证WebLogic和WebSphere的Adaptor工作过程与此是完全一致的),看看反编译后的代码。通过查看inetpub\wwwroot\arcgis\Web.config文件得知,IIS中的Web Adaptor主要的工作过程都集中在ESRI.ArcGIS.WebAdaptor.dll(在GAC目录中)里。这个dll里首先找到两个类:
image  一个Node就对应一个GIS Server机器,_healthy标记该机器的可用状态;NodeManager是所有Node的管理类。Web Adaptor启动时,会调用WebAdaptorConfig.GetMachines()方法,此方法会向http://siteip:port/arcgis/admin/machines发送get请求,获取site内所有GIS Server机器的列表,然后利用UpdateNodeList方法保存在NodeManager类中,并写入WebAdaptor.config文件。
  此外,最重要的是一个叫AGSHandler的类,它实现了IHttpHandler接口:
image    发送到Web Server的GIS请求都会由其中的ProcessRequest方法进行处理。前面说过Web Adaptor是以轮询方式转发请求的,而_currentNodeIndex便记录了请求转发目标GIS Server的在Nodes列表中的索引:
image  Web Adaptor接收到发到Web Server的GIS请求后,首先会读取初始化时保存的WebAdaptor.config配置文件并查看里面存储的GIS Server机器列表,如果读取失败或者机器个数为0,都会响应500的错误。如1所示;接下来会利用try里的TransferRequest方法将请求转发到具体的GIS Server(的tomcat)上去,如果转发的这个机器没有响应,则会在catch中将此机器利用MarkUnHealthy方法标记为不可用;而不论当前节点是否成功接收请求,TransferRequest方法中都会将_currentNodeIndex节点加一,保证下一个请求发送到下一台GIS Server上去,以实现轮询请求的事实:
image image  最后来看看Web Adaptor是如何根据配置文件的间隔时间去追询标记为下线机器的状态,在机器上线后又将它们加回可用机器列表的:
imageimageimage  可以看出,Web Adaptor检测到没有响应的机器后,除了标记其为不可用,也会同时启动一个定时器,此定时器逝去配置文件中的检测下线机器状态时间间隔(分钟)后,会立即将这台机器重新标记为可用,而不会去检测它事实上是否可用。判断机器是否真正可用的过程还是通过前面TransferRequest方法来完成的,即直接向其转发请求,如正常响应,证明其已经在线;如不能正常响应,则再次标记其不可用,重新启动定时器进入下一个轮回…
  大家可以想一想,整个过程是否有不合理的地方呢?

2012年7月5日星期四

ArcSDE 10.1 for PostgreSQL on Windows安装

ArcGIS换了10.1后,需要安装SDE,按照以前的习惯依然选用PostgreSQL。但10.1的SDE安装步骤变化不小,记录下来供有需要的朋友参考。
首先,安装介质上不再自带PostgreSQL数据库的安装文件了,需要自己下载。系统要求上指明支持的版本是9.0.5 64bit(所有Enterprise SDE只支持64位的数据库),我在官网上下载了最接近的9.0.8版本,安装。
其次,10.1版本在弱化ArcSDE服务连接的事实,所有文档默认使用直连的方式。最明显的体现是,将以前的Post Installation的步骤(创建数据库用户,系统表,Schema等工作)从ArcSDE的软件安装步骤中剥离了出来,而能够(也仅能够)采用名为Create Enterprise Geodatabase的新GP工具来完成。因此,如果不需要ArcSDE服务连接方式的话,就无需安装ArcSDE软件了。
下面说明建立直连方式的完整安装步骤(参考此安装文档):
  1. 安装PostgreSQL数据库;
  2. (可选)修改%PostgreSQL%\9.0\data\pg_hba.conf文件,添加“host     all          all      0.0.0.0/0       md5”记录,让任意客户端均可连接你的数据库集群(修改后需重启postgresql-x64-9.0服务以生效);
  3. 从%Program Files%\ArcGIS\Desktop10.1\DatabaseSupport\PostgreSQL\Windows64目录下,拷贝st_geometry.dll文件到%PostgreSQL%\9.0\lib目录。在PostgreSQL中创建Geodatabase时必须用到此类库;
  4. 配置PostgreSQL的客户端。因为直连方式要求ArcSDE的客户端必须安装数据库的客户端类库,所以首先需要获取PostgreSQL的客户端。ArcGIS Desktop是32位软件,需要的是32位的PostgreSQL类库。我在官网上的zip archive链接中下载了32位的PostgreSQL binary文件,解压后将bin目录中的libeay32.dll, libiconv-2.dll, libintl-8.dll, libpq.dll, and ssleay32.dll文件拷贝到%Program Files%\ArcGIS\Desktop10.1\bin目录下;如果ArcGIS Server也需要直连到PostgreSQL中,则需要拷贝libeay32.dll, libintl.dll, libpq.dll, and ssleay32.dll上述同名的5个64位的库(帮助中的64位库列表有误)到%Program Files%\ArcGIS\Server\bin目录下,因为ArcGIS Server现在是64位的软件了,这几个库可在PostgreSQL 64位的安装目录下找到;
  5. 在ArcMap中使用Create Enterprise Geodatabase工具完成“Post Installation”过程。
    image
    对于PostgreSQL库来说Instance是数据库所在的服务器名称;Database是所要新建的GDB名称;对于PostgreSQL,GDB Administrator只能是sde;如果Tablespace Name留空,会将GDB创建到默认的pg_default表空间里;
  6. 建立直连连接。通过Add Database Connections建立直连连接:
    imageDatabase是刚才创建的GDB名称。
如果只需要使用直连方式连接的话,就到此结束了。如果仍需要使用ArcSDE服务方式连接,则需要其他步骤。下面说明建立ArcSDE服务连接的额外步骤:
  1. 安装ArcSDE for PostgreSQL 10.1;
  2. 修改%Program Files%\ArcGIS\ArcSDE\pgexe\etc\services.sde文件和Windows\System32\drivers\etc\hosts文件,分别增加“esri_sde 5151/tcp”记录;
  3. 创建ArcSDE服务。你会发现1中安装完ArcSDE后没有弹出后配置的对话框,也无法从开始菜单中找到它,sdesetup命令也没有了install的操作,这也是前面为什么说这个工作仅能够通过新的GP工具来完成的原因。所以以前后配置向导中创建ArcSDE服务的工作就只能通过命令行方式来完成了,分别键入以下两个命令以完成在注册表中创建服务的工作:
    sdeservice -o create -d POSTGRESQL,postgresql-x64-9.0
    sdeservice -o register -d POSTGRESQL,postgresql-x64-9.0 -r ADMIN_DATABASE -v sde
  4. 启动服务。可以用“sdemon –o start”命令或从Windows服务面板中启动ArcSDE服务;
  5. 创建连接。Add Database Connection只能创建直连连接,因此需要使用新的Create ArcSDE Connection File GP工具来创建ArcSDE服务连接文件。为了保持以前版本的使用习惯,可将新创建的.sde文件位置保存在c:\Users\Administrator\AppData\Roaming\ESRI\Desktop10.1\ArcCatalog\目录中,这样就可以从Database Connections节点中方便的找到它了。
    image
参考:http://blog.csdn.net/linghe301/article/details/6675044

2012年6月12日星期二

2012年6月3日星期日

vimeo转载:homeland

from:https://vimeo.com/31413518

背景音乐:Welcome Home_Radical Face

2012年6月1日星期五

GIS中公交数据的下载和处理

  前两天讲座的Demo中,演示了一个可以在等公交时查看将要到站车辆信息的移动应用,其中用到了北京若干条公交线路的数据。不少朋友都想知道这些数据是如何获取的,我把方法共享出来,希望能为大家GIS应用的道路上扫清一些不必要的障碍。
  为了获取公交数据,我对比了几个具有查询公交线路功能的在线地图网站,查看http的请求结果,发现百度地图的http响应最接近成品状态,它的结果是标准的json数据,含有查询公交线路上所有的站点信息和公交路线信息等。简单验证了一下,如果从百度地图网站上发起请求,可得到所有明文信息,如果从其他站点发起请求,所有涉及到地理坐标的值会被加密。
  我想完成的事情是,获取关心的公交线路数据,然后把他们做成Geodatabase中的FeatureClass来使用。只要能得到有效的JSON数据,就可以利用ArcGIS中的Python脚本来完成我想要的结果。
  第一步需要获取JSON数据。为了获取批量的结果,我们需要使用百度地图的API。我学习了酸奶小妹妹写的一篇现成的文章,知道了从Javascript中可获取我想要的JSON信息。下一步就是如何将js中的JSON结果保存成文本文件。js直接写txt不太好办,借助Silverlight与html内容的交互功能,就可以办到这点。我写了一个Silverlight应用来完成这个工作,首先填写想要查询的目标城市,比如“西安”,然后填写想要查询的公交线路,多条线路间需要用英文半角逗号隔开,点击“开始下载”就会从百度得到JSON结果,保存成txt文件以备后用。
  第二步需要用Python处理JSON数据以生成FeatureClass。我写了一个python脚本来完成这个工作,并把它制作成了Script Tool,这样就可以通过调用GP工具的形式来生成我们的地理数据了。
  第三步数据后处理。百度API中得到的地理数据是经纬度坐标,生成FeatureClass后可利用Project工具投影到我们想要的坐标系中,比如WGS 1984 Web Mercator。对于火星坐标,一个城市范围内的偏移可以认为是线性的,只需在Arcmap中参照底图整体平移即可。
  比如,我在这个网站上参照页面源码可以得到西安市所有公交线路列表,简单处理后变成这样:

地铁1号线,地铁2号线,地铁3号线,地铁4号线,地铁5号线,地铁6号线,机场快轨,环山1号线,环山2号线,二环1号线,二环2号线,高新1号线,高新草堂专线,教育专线,五龙专线,泾渭环线,1,2,4,K5,6,7,K8,9,10,11,12,13,14/K14,15,16,17/K17,K18,19,20,20区间,21,22,23,24,25,26,27,28,29/K29,30/K30,31,32,33,34,K35,36,37,38,39,40,41,42,K43,44,45,46/K46,47,48,50,102,103,104,105,106,107,108,117,118,161,162,163,201,202/K202,K203,204,K205,206,207,207区间,K208,209,K210,211,212,213,214,215/K215,215区间,216,217,218,219,220,22,222,223,224,225,226,226区间,227,K228,229,230,230区间,231,232,233,234,K235,236,237,238,239,240,241,242,243,251,252,260,260区间,261,262,263,264,265,266,300/K300,301,302,303,307,308,309,311,312,313,315,316,318(车城),318(全程),319,321,322,323,324,规划325,规划326,328,330,331,336,351,规划356,K400,401,402,403,405,406,407,408,409,410,411,416,500,501,502,503,504,506,507,508,509,510,511,512,规划513,规划515,517,518,519,521,522,523,525,526,527,528,530,600,601,602,603,604,604区间,K605,606,606区间,607,608,K609,611,612,K616,K618,619,K630,630区间,K631,K700,701,702,703,704,705,706,707,708,709,710,711,712,713,714,715,716,717,718,719,720,721,722,723,728,729,800/K800,801,规划802,规划821,规划822,900,900区间,901,901区间,902,903,904,905,906,907,908,910,911,912,913,914,915,916,917,918,919,920(汤峪),920(焦岱),921,922,923,924,926,927,928,929,930,东南专线,宏华线,安运线,西高客运,规划西阎线,通宵1号线,通宵2号线,通宵3号线,通宵4号线,游1,游2,游3,游4,游5(306),游6,游7,游8(610),游9,游10,盛唐之旅,曲江新景线,曲江旅游观光轻轨,世园环线,世园1号线,世园2号线,世园3号线,世园4号线,世园5号线,世园6号线,世园7号线,世园8号线,世园9号线,世园10号线,世园12号线,世园13号线,3-18,4-04,4-05,4-06,4-07,4-08,4-09,4-10,4-11,4-12,4-13,4-14,4-15,4-16,4-17,4-18,4-19,4-20,4-21,4-22,4-23,4-25,临潼101,临潼102,临潼201,临潼301,临潼826,阎良601,阎良901,高陵101,高陵102,高陵103,高陵105,高陵106,高陵108,高陵109,高陵201,高陵801,高陵802,高陵803,户县801,户县802,户县803,户县804,户县805,户县806,户县808,户县809,户县810,户县811,户县812,户县813,户县819,户县820,咸阳13,咸阳15,咸阳21,咸阳22,咸阳28,咸阳29,咸阳59,咸阳副59,
  利用这个Silverlight应用即可下载到JSON结果,再用一下GP工具,就可生成西安市公交线路的地理数据。
image
imageimage
image  当然你也可以把它导出成shapefile,方便其他同事,同学或老板使用。
  GP工具,Python脚本,示例FeatureClass可在这里下载:http://arcgis.com/home/item.html?id=e0f8316d91fb43d49a81a76946f9a03c

2012Esri中国开发者大会讲座:Silverlight及Windows Phone部分的资料

ppt,demo源码下载:http://arcgis.com/home/item.html?id=cf6c35fe15f74e69a99a2253cd72aeab

Silverlight部分:

Windows Phone部分:

2012年4月25日星期三

Portable Basemap Server发布2.0.5版本

  更新内容:
~为数据源类型为ArcGISDynamicMapService的PBS服务增加附加参数设置,比如layers,layerDefs等,达到控制图层可见性,按属性过滤图层内容等目的;
~REST Admin API为ArcGISDynamicMapService数据源增加changeParams操作,以动态修改附加参数。详见这里;
~数据源增加对ArcGISTiledMapService的支持;
~增加自定义在线地图功能。可通过修改CustomOnlineMaps.xml文件内容,自行增删在线地图数据源(数据源须采用Google Maps/Bing Maps/ArcGIS Online地图的缓存策略);
~增加自动保存/载入上次配置的功能;
~为系统托盘图标增加右键菜单;
~修复已知bug;
  详见:http://blog.newnaw.com/?p=890
  再加入一些功能后,Portable Basemap Server将会在下个版本开源。

2012年3月31日星期六

ArcGIS Runtime性能

  可能你已经了解到了一些要随ArcGIS 10.1版本发布的新产品ArcGIS Runtime的信息,比如它的跨平台,高便携,64位,可扩展等。
  Esri刚刚结束的developer summit上,以WPF为例,演示了最新的prerelease版本中ArcGIS Runtime的性能。视频中prerelease版中同时加载了10,000个带动画的复杂渲染的graphic。

  Web API中一直被偏执狂们诟病的graphic性能有了长足的提升,ArcGIS Runtime更加让人期待了。
  ps:期待更多有社会价值的公交车,出租车,物流监控系统出现。

2012年3月15日星期四

弟子规-与鲶总、李总、法人共勉

总 叙

弟子规 圣人训 首孝悌 次谨信

泛爱众 而亲仁 有余力 则学文

入 则 孝

父母呼 应勿缓 父母命 行勿懒

父母教 须敬听 父母责 须顺承

冬则温 夏则凊 晨则省 昏则定

出必告 反必面 居有常 业无变

事虽小 勿擅为 苟擅为 子道亏

物虽小 勿私藏 苟私藏 亲心伤

亲所好 力为具 亲所恶 谨为去

身有伤 贻亲忧 德有伤 贻亲羞

亲爱我 孝何难 亲憎我 孝方贤

亲有过 谏使更 怡吾色 柔吾声

谏不入 悦复谏 号泣随 挞无怨

亲有疾 药先尝 昼夜侍 不离床

丧三年 常悲咽 居处变 酒肉绝

丧尽礼 祭尽诚 事死者 如事生

出 则 悌

兄道友 弟道恭 兄弟睦 孝在中

财物轻 怨何生 言语忍 忿自泯

或饮食 或坐走 长者先 幼者后

长呼人 即代叫 人不在 己即到

称尊长 勿呼名 对尊长 勿见能

路遇长 疾趋揖 长无言 退恭立

骑下马 乘下车 过犹待 百步余

长者立 幼勿坐 长者坐 命乃坐

尊长前 声要低 低不闻 却非宜

进必趋 退必迟 问起对 视勿移

事诸父 如事父 事诸兄 如事兄

朝起早 夜眠迟 老易至 惜此时

晨必盥 兼漱口 便溺回 辄净手

冠必正 纽必结 袜与履 俱紧切

置冠服 有定位 勿乱顿 致污秽

衣贵洁 不贵华 上循分 下称家

对饮食 勿拣择 食适可 勿过则

年方少 勿饮酒 饮酒醉 最为丑

步从容 立端正 揖深圆 拜恭敬

勿践阈 勿跛倚 勿箕踞 勿摇髀

缓揭帘 勿有声 宽转弯 勿触棱

执虚器 如执盈 入虚室 如有人

事勿忙 忙多错 勿畏难 勿轻略

斗闹场 绝勿近 邪僻事 绝勿问

将入门 问孰存 将上堂 声必扬

人问谁 对以名 吾与我 不分明

用人物 须明求 倘不问 即为偷

借人物 及时还 后有急 借不难

凡出言 信为先 诈与妄 奚可焉

话说多 不如少 惟其是 勿佞巧

奸巧语 秽污词 市井气 切戒之

见未真 勿轻言 知未的 勿轻传

事非宜 勿轻诺 苟轻诺 进退错

凡道字 重且舒 勿急疾 勿模糊

彼说长 此说短 不关己 莫闲管

见人善 即思齐 纵去远 以渐跻

见人恶 即内省 有则改 无加警

唯德学 唯才艺 不如人 当自砺

若衣服 若饮食 不如人 勿生戚

闻过怒 闻誉乐 损友来 益友却

闻誉恐 闻过欣 直谅士 渐相亲

无心非 名为错 有心非 名为恶

过能改 归于无 倘掩饰 增一辜

泛 爱 众

凡是人 皆须爱 天同覆 地同载

行高者 名自高 人所重 非貌高

才大者 望自大 人所服 非言大

己有能 勿自私 人所能 勿轻訾

勿谄富 勿骄贫 勿厌故 勿喜新

人不闲 勿事搅 人不安 勿话扰

人有短 切莫揭 人有私 切莫说

道人善 即是善 人知之 愈思勉

扬人恶 即是恶 疾之甚 祸且作

善相劝 德皆建 过不规 道两亏

凡取与 贵分晓 与宜多 取宜少

将加人 先问己 己不欲 即速已

恩欲报 怨欲忘 报怨短 报恩长

待婢仆 身贵端 虽贵端 慈而宽

势服人 心不然 理服人 方无言

亲 仁

同是人 类不齐 流俗众 仁者希

果仁者 人多畏 言不讳 色不媚

能亲仁 无限好 德日进 过日少

不亲仁 无限害 小人进 百事坏

余 力 学 文

不力行 但学文 长浮华 成何人

但力行 不学文 任己见 昧理真

读书法 有三到 心眼口 信皆要

方读此 勿慕彼 此未终 彼勿起

宽为限 紧用功 工夫到 滞塞通

心有疑 随札记 就人问 求确义

房室清 墙壁净 几案洁 笔砚正

墨磨偏 心不端 字不敬 心先病

列典籍 有定处 读看毕 还原处

虽有急 卷束齐 有缺坏 就补之

非圣书 屏勿视 蔽聪明 坏心志

勿自暴 勿自弃 圣与贤 可驯致

命自我作 福自我求

心好命又好 富贵直到老

命好心不好 福变为祸兆

心好命不好 祸转为福报

心命俱不好 遭殃且贫夭

心可挽乎命 最要存仁道

命实造於心 吉凶惟人召

信命不修心 阴阳恐虚矫

修心一听命 天地自相保

2012年2月29日星期三

在新的Android工程中调用已经编译好的so库文件代码

依旧是Android,NDK,JNI相关的问题。
  照着ndk帮助的说明,运行<ndk>/samples目录下的hello-jni工程后,你一定想知道如何在自己的项目里调用别人已经编译好的.so库文件。于是新建一个Android工程,将hello-jni例子中ndk-build后的结果:libs文件夹(包括其下所有内容)拷贝到新建工程的根目录下,仿照hello-jni的代码,调用native方法:
   1: package com.my.SoTest;
   2:  
   3: import android.app.Activity;
   4: import android.os.Bundle;
   5: import android.widget.TextView;
   6:  
   7: public class SoTestActivity extends Activity {
   8:     /** Called when the activity is first created. */
   9:     @Override
  10:     public void onCreate(Bundle savedInstanceState) {
  11:         super.onCreate(savedInstanceState);
  12:         
  13:         TextView tv=new TextView(this);
  14:         tv.setText(stringFromJNI());
  15:         setContentView(tv);
  16:     }
  17:     public native String stringFromJNI();
  18:     static {
  19:         System.loadLibrary("hello-jni");
  20:     }
  21: }

  运行后,logcat记录以下错误:


No implementation found for native Lcom/my/SoTest/SoTestActivity;.stringFromJNI ()Ljava/lang/String;
...
java.lang.UnsatisfiedLinkError: stringFromJNI

  在hello-jni的代码注释里明确说明,UnsatisfiedLinkError错误是由于native代码中没有该方法的实现而引起的,但stringFromJNI这个方法,在c代码中是有的;前一个错误就比较明确了,是在Lcom/my/SoTest/SoTestActivity中去找这个方法实现的,打开hello-jni.c文件看看该函数头,其中有这样的字样:Java_com_example_hellojni_HelloJni_stringFromJNI,而com.example.hellojni恰好是例子工程的package name,HelloJni则是class的名称。也就是说,我们.so中函数声明涉及到的package name和class name与调用它的package name和class name不符。google一下印证了这个猜测,但提供现成解决办法的不多,总不能自己工程的package name必须和.so里的一样吧。
  搜索看到了一篇NDK的教程,Using NDK to Call C code from Android Apps(需翻墙),里面写明了在一个普通Android工程中,如何从零开始组装自己的native代码。大致的步骤是,首先写一个java类,里面声明一些需要实现native方法(函数签名即可),然后用javah命令来生成对应的header文件,里面的内容是自动生成的native函数声明,然后照着这个声明实现各个函数,最后编译,用最初的java类调用。
  受此启发,我们在拿到别人已经编译好的.so文件后,可首先新建一个java类,所在package的名称和class名称都与.so文件中函数签名提示的一致,在这个类中加入native方法的声明。这样在别处就可以用这个wrapper调用so库中的函数了。

2012年2月27日星期一

在Android上利用NDK编译并使用Spatialite库(Windows环境)

  Sqlite数据库因其体积便携(基于单个文件,跨平台),功能完整(类似RDBMS)受到非常广泛的欢迎,在iOS,Android平台上都提供原生的支持。Spatialite是Sqlite的一个空间扩展,根据官方介绍,Spatialite之于Sqlite,相当于PostGIS之于PostgreSQL。
  Spatialite扩展提供了一系列用于空间操作的SQL函数,例如基础的空间查询,缓冲区生成等,也有高级的类似ArcObjects中ITopologicalOperator,IRelationalOperator等接口的功能。
  在Android平台上使用Spatialite资料比较少,主要是google上的Spatialite for Android和github上的android-spatialite两个工程。前者带有完整的编译步骤,我尝试了这个,其中遇到了些问题,记录下来,方便需要用到的朋友。后者工程似乎带有编译好的libjsqlite.so库,可直接使用。
  我机器的环境是Windows 7 64bit,已经搭建好了Android Eclipse开发环境。由于Spatialite及Sqlite是C/C++的native语言编写,而Android的默认开发语言是Java,所以在Android上编译Spatialite必须用到google提供的NDK(Native Development Kit)环境。NDK环境的搭建比较简单,按照google的文档进行即可:

  1. 下载NDK压缩包(android-ndk-r7b-windows.zip)。我下载的是最新的android-ndk-r7b版本。将其解压到任意目录,下文以<ndk>指代。注意:由于后面cygwin中配置环境变量时不支持带空格的路径,所以建议将其解压到某个盘符的根目录下,比如d:\android-ndk-r7b\;
  2. 设置Windows的环境变量。主要是将NDK的路径添加到PATH中去,方便在任何位置调用ndk-build命令。详细操作不再赘述。此步骤只是为cmd提供了便利;
  3. 由于NDK需求中说明,必须用到GNU Make命令,所以在Windows环境上还需要提前安装Cygwin。这是在Windows上模拟Linux环境的工具。安装步骤参考此贴:Windows环境下Android NDK环境搭建。注意在cygwin的linux环境下,配置$NDK环境变量,方便在任何地方调用ndk-build编译命令。我在安装Cygwin时,只额外勾选了Devel库。

  完成这个步骤后,可尝试编译运行NDK中自带的hello-jni例子,验证NDK编译环境已经搭好。具体步骤参见google的文档。可以发现,native代码需要放在与src同级的jni目录下,该目录下必须至少有Android.mk文件,向NDK系统描述源文件;编译时需要在工程的根目录下运<ndk>/ndk-build命令;利用NDK编译后,Android工程文件夹中会多出libs目录,里面会生成lib<something>.so共享库,这就是我们要用到类库,该文件也会一并打包到apk文件中去。更多的ndk知识请参考<ndk>/documentation.html文件,非常详尽。注:JNI相当于.net平台的p/invoke。
  接下来就要按照Spatialite for Android上的提示,在cygwin环境中编译spatialite库了。

  1. 在windows下利用svn工具对整个project checkout(这次作者用的是Spatialite 3.0.1,PROJ 4.7.0,geos 3.2.2几个库)。会发现有两个工程spatialite-android和spatialite-android-test。我们主要对spatialite-android工程进行编译;
  2. 由于spatialite用到了geos和proj两个库,所以首先需要为该工程下载,配置两个库。具体步骤如下,在cygwin环境中执行:
       1: $ cd jni
       2: $ wget http://download.osgeo.org/geos/geos-3.2.2.tar.bz2
       3: $ tar xvjf geos-3.2.2.tar.bz2
       4: $ cd geos-3.2.2
       5: $ ./configure --build=x86_64-pc-linux-gnu --host=arm-linux-eabi
       6: $ cd ..
       7: $ wget ftp://ftp.remotesensing.org/proj/proj-4.7.0.tar.gz
       8: $ tar xvzf proj-4.7.0.tar.gz
       9: $ cd proj-4.7.0
      10: $ ./configure --build=x86_64-pc-linux-gnu --host=arm-linux-eabi
      11: $ touch src/empty.cpp
      12: $ cd ..

  3. 按照作者的提示,下来就可以直接对工程进行ndk-build了(注:此时仍然在spatialite-android/jni目录下,而非spatialite-android目录)。如果你这么做了,会出现一个错误:


       1: ...
       2: Compile thumb  : proj <= rtodms.c
       3: Compile thumb  : proj <= vector1.c
       4: StaticLibrary  : libproj.a
       5: Compile++ thumb  : geos <= geos_c.cpp
       6: In file included from D:/spatialite-android/jni/geos-3.2.2/source/headers/geos/geom/Coordinate.h:20,
       7:                  from D:/spatialite-android/jni/geos-3.2.2/source/headers/geos/g                                                                                                                eom/Envelope.h:26,
       8:                  from D:/spatialite-android/jni/geos-3.2.2/source/headers/geos/i                                                                                                                ndex/strtree/STRtree.h:27,
       9:                  from D:/spatialite-android/jni/geos-3.2.2/capi/geos_c.cpp:19:
      10: D:/spatialite-android/jni/geos-3.2.2/source/headers/geos/platform.h:29:20: error                                                                                                                : ieeefp.h: No such file or directory
      11: /cygdrive/d/android-ndk-r7b/build/core/build-binary.mk:243: recipe for target `/                                                                                                                cygdrive/d/spatialite-android/obj/local/armeabi/objs-debug/geos/geos-3.2.2/capi/                                                                                                                geos_c.o' failed
      12: make: *** [/cygdrive/d/spatialite-android/obj/local/armeabi/objs-debug/geos/geos                                                                                                                -3.2.2/capi/geos_c.o] Error 1
    image
    提示ieeefp.h: No such file or directory。要修正这个错误,你需要在spatialite-android/jni/geos-3.2.2/source/headers/geos/platform.h文件中大约15行的位置,找到“#define HAVE_IEEEFP_H 1”这句话,并在其下面添加一行“#undef HAVE_IEEEFP_H”,取消对HAVE_IEEEFP_H的定义。保存该文件;

  4. 再次ndk-build,这回发现上面的错误消失了。但又产生了新的错误:


       1: ...
       2: Compile++ thumb  : geos <= InteriorPointLine.cpp
       3: Compile++ thumb  : geos <= InteriorPointPoint.cpp
       4: Compile++ thumb  : geos <= LineIntersector.cpp
       5: D:/spatialite-android/jni/geos-3.2.2/source/algorithm/LineIntersector.cpp: In st                                                                                                                atic member function 'static double geos::algorithm::LineIntersector::interpolat                                                                                                                eZ(const geos::geom::Coordinate&, const geos::geom::Coordinate&, const geos::geo                                                                                                                m::Coordinate&)':
       6: D:/spatialite-android/jni/geos-3.2.2/source/algorithm/LineIntersector.cpp:224: e                                                                                                                rror: expected unqualified-id before '(' token
       7: D:/spatialite-android/jni/geos-3.2.2/source/algorithm/LineIntersector.cpp:232: e                                                                                                                rror: expected unqualified-id before '(' token
       8: D:/spatialite-android/jni/geos-3.2.2/source/algorithm/LineIntersector.cpp: In me                                                                                                                mber function 'void geos::algorithm::LineIntersector::computeIntersection(const                                                                                                                 geos::geom::Coordinate&, const geos::geom::Coordinate&, const geos::geom::Coordi                                                                                                                nate&)':
       9: D:/spatialite-android/jni/geos-3.2.2/source/algorithm/LineIntersector.cpp:304: e                                                                                                                rror: expected unqualified-id before '(' token
      10: D:/spatialite-android/jni/geos-3.2.2/source/algorithm/LineIntersector.cpp:306: e                                                                                                                rror: expected unqualified-id before '(' token
      11: D:/spatialite-android/jni/geos-3.2.2/source/algorithm/LineIntersector.cpp: In me                                                                                                                mber function 'int geos::algorithm::LineIntersector::computeIntersect(const geos                                                                                                                ::geom::Coordinate&, const geos::geom::Coordinate&, const geos::geom::Coordinate                                                                                                                &, const geos::geom::Coordinate&)':
      12: D:/spatialite-android/jni/geos-3.2.2/source/algorithm/LineIntersector.cpp:426: e                                                                                                                rror: expected unqualified-id before '(' token
    image在很多位置都提示 error: expected unqualified-id before '(' token,google一下这个错误,发现大概是由于在头文件中对某个函数重复声明冲突引起的。于是打开spatialite-android/jni/geos-3.2.2/source/algorithm/LineIntersector.cpp文件查看,发现基本都与一个名为“ISNAN”的函数有关。在Windows下搜索包含此字符串的文件,又来到了刚才修改过的platform.h文件中,发现这个文件中有与此函数相关的语句。为了解决此问题,尝试对此文件进行再次修改。在大约25行的位置,可以找到“/* #undef HAVE_ISNAN */”这句话,在这句话下面添加一句“#define HAVE_ISNAN 1”。这样做的目的是改变该文件后续的一些编译处理。

  5. 最后再次ndk-build,成功输出libjsqlite.so库。
    image

  现在我们在eclipse中运行spatialite-android这个工程(需要将test-2.3.sqlite数据库adb push到模拟器的sd卡上),就可以看到预期的结果了。
image  关于如何利用java语言调用spatialite的功能,可以详细参考这个工程中jsqlite包下的封装代码。至此我们就完成了在Android平台上使用spatialite库的准备工作。
  此次编译中遇到的问题可参考:http://code.google.com/p/spatialite-android/issues/detail?id=3