2009年12月29日星期二

关于台湾的几个视频

        这是台湾在捷克的留学生做的几个介绍台湾的短片,放出来,有助于大家从台湾的角度更好的了解台湾。
        ps:请理性勿妄论。

Taiwan History:

Traditional Culture:

Delicacy, Celebrities & IT:

Something More about Taiwan:

Game Time:


        原文链接:http://chinese.engadget.com/2009/12/27/taiwan-presentation-by-czech/

2009年12月28日星期一

二套房

        淘宝购托架,赛格买硬盘,电脑在于折腾。
        抽走光驱,给小黑加了第二块硬盘WD5000BEVT,320+500,地方好大哟~现在本本是双(核)cpu,双内存,双硬盘,(暂时)双系统,差不多到头了。如果SSD以后降价,小黑还在服役的话,那就是最后的折腾了。

2009年12月21日星期一

2010来了

        今天去医院看望住院的爷爷。去的时候他还在休息,不一会就坐了起来。
        爷爷说,家里有个新照相机(七八年前买的,一直没打开用),放那可惜了,有时间让我教他用用;现在院子里统一要换数字电视,可两个遥控器他用不来,他只想要原来的六个台,中央台,陕西台和西安台,看看新闻听听秦腔就可以;到现在也没摸过一下电脑,现在年轻人很懒,把自己的脑子用好就行了;以前去日本,东京的包子要15块钱一个,不过街道很干净,可以席地而坐,可惜没去看富士山;刚工作的时候工资是4块钱,自己是写文章的好手,但现在同样的文章却很难看懂,不信拿我的论文去给他看看;家里的东西多,过去攒了好多小东西,其实到现在也一直没用上,回去就好好清理一番……
        爷爷喝了两杯茶水,觉得很香,下午阳光很好。
        有一首诗叫《老来难》,相传是唐代杜牧公元775年时候所作。语言通俗,描述细腻,道尽老年人的生活特点和万般苦痛,劝人要孝敬老人,尊重老人,并委婉告诉人们,人人都要经过老年这一阶段,孝敬老人也是尊重自己。我找到了一个小河演唱的版本,作曲也是小河。

        歌曲在上面,词在这里:老来难,老来难,少年莫把老人嫌。当初只嫌别人老,如今轮到我头前。千般苦,万样难,听我从头说一番。耳聋难与人说话,插七插八惹人嫌。雀蒙眼,似鳔粘,鼻泪常流擦不干。人到面前看不准,常拿李四当张三。年轻人,笑话咱,说我糊涂又装憨。亲朋老幼人人恼,儿孙媳妇个个嫌。牙又掉,口流涎,硬物难嚼囫囵咽。一口不顺就噎着,卡在嗓喉噎半天。真难受,颜色变,眼前生死两可间。儿孙不给送茶水,反说老人嘴好馋。鼻子漏,如脓涎,常常流落胸膛前。茶盅饭碗人人恶,席前陪客个个嫌。头发少,顶门寒,凉风飕的脑袋酸。冷天睡觉常戴帽,拉被蒙头怕风钻。侧身睡,翻身难,浑身疼痛苦难言。盼明不明睡不着,一夜小便六七番。怕夜长,怕风钻,时常受风病来缠。老来肺虚常咳嗽,一口一口吐粘痰。儿女们,都恨咱,说我邋遢不像前。老得这样还不死,你要在世活千年。脚又麻,腿又酸,行动坐卧真艰难。扶杖难行二三里,上炕如同登泰山。无心记,糊涂缠,常拿初二当初三。提起前来忘了后,颠三倒四惹人烦。年老苦,说不完,仁人君子仔细参。莫要嫌,莫要嫌,人生不能常少年。今日少年转眼老,人人都有老来难!
        前些日子一个好朋友送给我一本龙应台的《孩子,你慢慢来》,问我是否同意书中的观点,我看完后觉得同意得不能再同意了。我顺便在推荐一本书《苏菲的世界》,最近看过的两个动画片《暴力云与送子鹤》《Mary and Max》,送给有童心的人。又有谁没有童心呢?
        老人和小孩,都是那么的可爱,都一样需要关怀和照顾。
        前一阵听广播,有个人失恋的同时又丢了工作,他说难道世上还有比我更可怜的人么?我觉得真没有了,如果你自己觉得你是最可怜的人,那你一定就是了。高中的课文《假入给我三天光明》,也许还体会不到失明的苦楚,可是“假如给你三天黑暗”呢?如果坚持闭着眼睛或者带着眼罩活动一天,那一定会令人抓狂的。有时我会想,如果将来我失明了,或者截肢了,我能够再做些什么呢?像海伦凯勒那样写书么?不能。像周云蓬那样去写歌么?不能。以前想到这些我就觉得可怕,害怕自己失去一些东西以后变的一无是处。但现在我至少想到了一条出路,我可以去边远山区教书。这样就能够挽回一些自己的愧疚之心,因为每当在电视上看到那些老师的事迹后我都会觉得自己好像欠谁点什么。在那里可以做一个真正意义上的老师,没有学术造假没有利益纠纷没有惺惺丑态没有虚伪善变,每天一看到孩子们渴望知识,渴望新鲜的大眼睛,就恨不得把自己的一切都掏出来全给他们。有东西吗?有一些。够吗?不够。所以至少为了这个目的,在自己身体还健全的时候得努力在积累点东西。
        昨天把越狱最后几集看完了,主角们逃亡了四年后终于可以休息了。发现最后结尾的地方很熟悉,突然想起来凡是有关自由的影片最后往往都用蓝蓝的大海作为结尾。《越狱》,《肖申克的救赎》,《当幸福来敲门》,《碧海蓝天》……李开复说人的价值不在于拥有什么,而在于他留下了什么。仔细想想我好想没给谁留下多少东西,但是我觉得这也不要紧,因为我至少不用疲于奔命,至少衣食无忧,至少身心健康,这样就还有机会,还有机会在明年的时候留下些东西。farewell,2009!

2009年12月19日星期六

哥本哈根疯会


        持续了两个星期的哥本哈根气候峰会今天就要结束了,可各国仍然没能达成具有实质性的共识(链接1链接2)。说到底,192个国家和地区的代表,11个利益集团,各自都在坚持自己利益的情况下所做出的让步根本无法促成一个实质性共识。对于这点我是心有余力不足哇。
        只能在这里给大家提供一些客观、公正的消息来源,希望可以有助于大家更好的了解这个会议:
        Q&A: Copenhagen climate change conference 2009,来自英国卫报,有:什么是哥本哈根气候峰会,主要参与方有哪些,通过它本想达成什么共识,争论主要在哪。
        Q&A: The Copenhagen climate summit,来自BBC新闻,与上面互为佐证,证明它们的观点都没有失实。
        Who’s at the Climate Talks, and What Do They Seek?,来自纽约时报,参加峰会的各个利益集团,它们的背景,各自想要什么。值得注意的是,中国已经在2007年超越美国成为全球最大的温室气体排放国家
        Global Warming,纽约时报的专题,从其中能更加全面地了解全球气候变化的背景。
        土摩托日记,非常值得尊敬的一位老师,自己亲自参与峰会,但未获记者证,只能尽力进入会场,为大家带来前方报道。
        ps:因为这篇文章,我觉得看新闻还是得全面些,避免因为信息获取不真实而歪曲了对事实的看法。
        pps:假如我是一只北极熊的话,我会这么想:原来再自己的地盘上玩耍是多么快乐的事情,不用写作业,不用买房子,饿了的时候就下水去捕鱼吃,想吃什么捉什么,吃饱了睡一觉,起来继续玩耍;可现在呢,我想吃的那种鱼怎么越来越难找了?玩耍的地方怎么越来越小了?不时的有巨大的铁家伙游入我的视线,它们是哪来的呢?还有就是,现在天气怎么变的这么热啊!对于我的孩子,关于这些,我能告诉她们什么呢?她们要问我的话,我也只好说:哎,你老爸我也搞不清楚这是咋回事呀,还是多练练游泳吧,以后游泳的机会可能会越来越多了,sigh...

2009年12月5日星期六

逼宫

        逼宫(来自百度百科):指大臣强迫帝王退位。也泛指强迫政府首脑辞职或让出权利。最近看了两则关于此关键词的新闻,网上评论很多,就不发表愚见了,记录一下。
        新闻1:外媒关注中国天然气荒
        本人按一下:中石化、中石油两公司顶住巨大压力,竭尽所能终于保证了千千万万人民用气需要,感谢他们八辈祖宗,好人有好报,祝他们一路走好。
        新闻2:杭州楼市频频被开盘
        再按一下:开发商们真不容易,为了不让群众们饱受寒风吹袭,宁可顶着失去信誉的压力提前开盘,好人有好报,也祝他们一路走好。

2009年11月29日星期日

This is it

        即使你一直很努力,极力想要改变一些东西,但最终不得不屈服于弄人的命运。
        Iverson走到了自己的一个十字路口。
        真正喜欢上篮球这个运动是在2000年的时候。中考前的几天,看到了76人与湖人的总决赛,看到了传说中的Allen Iverson。NBA中190以下的控卫并不多,虽然作为96年状元,但身高只有183的Allen,进入联盟的时候就注定了他与别人差了一大截,更别说其他位置的球员了。但他不服输的性格时刻提醒着自己一直在努力,甚至是拼命,往往要不顾一切的冲入禁区,直面身高甚至超过自己半米的中锋,然后把球送入篮筐。勤奋的练习加上顽强的性格,吸引了很多上进的年轻人。
        但也是因为他的性格,给了他一路走来的寂寞。他会靠自己的努力去带动全队,落后时把责任都抗在自己肩上,拼尽全力去和敌人肉搏,勇气可嘉,但结果有好有坏。有人批评他不信任队友,但在他看来是没有找到能够依靠的人。如果他只想做一名角色球员,放弃自己对总冠军的追求,那凭他的能力应该很容易获得满足。但13年如一日他坚持着自己对冠军的追求,大家看到的是他耀眼的数据,但只有他自己知道一路走来吞咽了多少苦果。
        他也曾与自己的梦想无限接近过。只有一次机会,他站在了总决赛的舞台上,眼前的奥布莱恩奖杯对于他来说就是莫大的幸福,触手可及。但他没能完成the one shot。因为:1、他不够成熟。他之前没有任何总决赛经验,仅凭着自己的想象去努力,拥有更多经验的OK组合变将梦想从他手上抢走了;2、客观条件严重限制了他的发挥。虽然穆托姆博正值当打之年,但与斯诺一样缺乏进攻能力,麦基也只能给他有限的帮助,对手又丝毫不给他更多的机会,注定了他只能败走洛杉矶。也许他在退役时也不能再拿到总冠军了,没有抓住那次绝好的机会,以后留给他的便是无限的遗憾。
        从今往后,他也许会在继续打球,但已经没有当初的决心和激情。即使他在某个队做替补,拿到了总冠军,那种感觉也会与他当初的追求相去甚远。一个对冠军如此渴望的人,虽然努力和拼搏,但终究还是只能眼睁睁看着别人捧走奖杯...

        对于他,扼腕痛惜,但命运不会同情他,像MJ的纪录片名一样,this is it,到此为止吧。
        贴上CCTV5的制作的片段,《离歌》作为bgm,再合适也不过了。

        在命运面前,你不得不跪下。

2009年11月27日星期五

Python魅力之地理数据快速处理方法

        Python作为一种成熟的脚本语言,发展势头迅猛,长期徘徊在“编程语言谱”排行前5的位置,在脚本语言中仅次于php。通过Python,开发人员可以快速试验一个想法、研究一种对象属性或不同算法,而无需编译、执行和测试任何代码。正是因为跨平台、简洁、优美的特点,它也如同GIS应用渗透于各各个行业一样,渗透在科学计算的各个领域。在GIS领域,几乎可以说开源言必称Python;而ArcGIS则将Python称为the scientific programming language for GIS:ArcGIS Server的猛将Geoprocessing Service依赖于Python,从9.4将Python IDE集成到ArcMap中也可见一斑。
        前一阵Flyingis贴出了一篇类似的文章,不过还是通过动手完成一个实例,来看看它在ArcMap中的数据快速处理能力吧。描述:上传到Panoramio的照片都会有位置信息(经纬度坐标),有些朋友非常可敬,上传的照片数量可达上万张。分析一下某个用户拍照的活动范围,是件有趣和有意义的事情。思路:1、利用Panoramio的API,获取指定用户的所有照片信息;2、将关心的信息,比如作者、照片名称、照片文件链接提取出来,并将其存储到地理数据库中。
        第一步:获取照片信息。先看一下Panoramio的API,REST风格,返回JSON字符串。比如通过http://www.panoramio.com/map/get_panoramas.php?order=popularity&set=public&from=0&to=20&minx=-180&miny=-90&maxx=180&maxy=90&size=medium,即可按点击次数排序获得世界范围内所有受欢迎的照片。

{
"count": 773840,"photos": [
{
"photo_id": 532693,
"photo_title": "Wheatfield in afternoon light",
"photo_url": "http://www.panoramio.com/photo/532693",
"photo_file_url": "http://static2.bareka.com/photos/medium/532693.jpg",
"longitude": 11.280727,
"latitude": 59.643198,
"width": 500,
"height": 333,
"upload_date": "22 January 2007",
"owner_id": 39160,
"owner_name": "Snemann",
"owner_url": "http://www.panoramio.com/user/39160",
},
{
"photo_id": 505229,
"photo_title": "Etangs près de Dijon",
"photo_url": "http://www.panoramio.com/photo/505229",
"photo_file_url": "http://static2.bareka.com/photos/medium/505229.jpg",
"longitude": 5.168552,
"latitude": 47.312642,
"width": 350,
"height": 500,
"upload_date": "20 January 2007",
"owner_id": 78506,
"owner_name": "Philippe Stoop",
"owner_url": "http://www.panoramio.com/user/78506"
}, ...
]
}


        Panoramio文档说有773840张,但是JSON里的count只有338张,搜其论坛,发现这是一个bug。当然,为了安全起见,一次请求最多只能返回100张照片的信息,Python刚好来完成若干个“100”的拼装任务。代码如下:

import arcgisscripting,json,urllib,os,threading
lphotos=[]#list saves all photo json objects
lock=threading.Lock()

#function to construct the panoramio request url and retrieve the corresponding photos as json array
#for eg: http://www.panoramio.com/map/get_panoramas.php?order=upload_date&set=139922&from=0&to=20&minx=108.355671725&miny=33.807692976&maxx=109.475020647&maxy=34.732003828&size=medium
#set: public (popular photos); full (all photos); user ID number
#size: original; medium (default value) ;small ; thumbnail ; square ; mini_square
def retrievephotos(set,fromindex,toindex,size):
global lphotos,gp
#extent=r"&minx=108.355671725&miny=33.807692976&maxx=109.475020647&maxy=34.732003828"
extent=r"&minx=-180&miny=-90&maxx=180&maxy=90"
url=r"http://www.panoramio.com/map/get_panoramas.php?order=upload_date&set="+set+"&from="+str(fromindex)+"&to="+str(toindex)+extent+"&size="+size
try:
jsonresult=json.read(urllib.urlopen(url).read())
except:
raise Exception,"get panoramio result error"
photos=jsonresult["photos"]
if len(photos)<toindex-fromindex:
print "warning: can't get photos from server "+str(fromindex)+"--"+str(toindex)
#gp.addwarning("warning: can't get photos from server "+str(fromindex)+"--"+str(toindex))#typeerror:'no type' object is not callable
else:
for photo in photos:
lock.acquire()
lphotos.append(photo)
lock.release()
return jsonresult["count"]#the total of photos of the specified set

#use multithread to retrieve photos, the number of threads=(end-start)/100
#start: 0 in usual
#end: the total of photos
def getphotolist(set,start,end,size):
list=[start]
while list[-1]<end:
list.append(list[-1]+100)
threads=[]
gp.addmessage(str(len(list))+" threads has launched...")
for i in list:
threads.append(threading.Thread(target=retrievephotos,args=(set,i,i+100,size),name=str(i)))
for t in threads:
t.start()
for t in threads:
t.join()#suspend the parent(main) thread until this thread is over

        在运行过程中,发送请求(请求100张照片的信息)后等待服务器响应会花费一些时间,于是就采用Python中的多线程,来同时发送多个请求。Threading是Python的一个标准模块,麻雀虽小,五脏俱全。单线程请求时,5000张照片的结果大约需要8分多钟,多线程请求时需要只需要40多秒即可。请求全部被相应后,即完成了所有照片信息的拼装工作:结果存储在类型为List的lphotos变量中。List是Python中使用最广泛的变量之一,灵活好用,一个顶5个~不过这里有个问题没有解决:在子线程的函数中,调用geoprocessor的addmessage()方法会报错(代码中注释掉的一句),非目前能力所能解决。
        题外话:上述多线程的工作也可以用并行计算来解决,这里有一篇出自Flyingis的关于Python中并行计算的文章,供大家学习。随着多核CPU的普及(ps:硅谷小公司推出100核CPU 性能是英特尔四倍),并行计算将会成为开发人员的新宠,微软.NET Framework 4.0中的Parallel Extention也说明了这一点。至于并行计算和多线程之间的区别,援引科班出身的同学胖胖鼠的话:
两者针对性不同。多线程技术是操作系统中的概念,是软件的概念。指进程中的一部分。以前并行计算主要更侧重硬件CPU,现在泛指同时使用多个计算资源解决问题。我感觉两者都用到了并发性并行性的概念。
个人感觉可简单理解为,并行计算是对细枝末节的多线程技术更优的抽象,与一个CPU里的多线程不同,它能利用多个CPU(不同服务器)资源,回归到网格计算的概念。下面继续主题。
        第二步:保存数据。之后我们便可以利用geoprocessor完成数据处理工作。首先通过调用Toolbox中的工具创建存储数据的FeatureClass,并调用工具添加相应的字段:

#create the 9.3 geoprocessor
gp=arcgisscripting.create(9.3)
gp.OverwriteOutput=True
myuserid=gp.getparameterastext(0)#"305320"
gp.Workspace=gp.getparameterastext(1)
myfile=os.path.basename(gp.getparameterastext(2))

#create 2 result featureclasses
try:
gp.addmessage("creating featureclasses...")
sr=gp.CreateObject("SpatialReference")
sr.Createfromfile(r"E:\Program Files\ArcGIS\Coordinate Systems\Geographic Coordinate Systems\World\WGS 1984.prj")
gp.Createfeatureclass(gp.workspace,myfile,"POINT","","","",sr)
gp.Addfield(myfile,"owner","TEXT","","",100)
gp.Addfield(myfile,"title","TEXT","","",100)
gp.Addfield(myfile,"photourl","TEXT","","",150)
#gp.Createfeatureclass(gp.workspace,xafile,"POINT",myfile,"","",sr)
del sr
except:
gp.adderror("creating featureclasses error")
raise Exception,"creating featureclasses error"

#push the photos into the featureclass
gp.addmessage("dealing my photos")
dealphotos(myuserid,myfile)

        最后两句便是整个功能的入口点了。dealphotos函数如下:

#function to push photos into featureclass
def dealphotos(set,fc):
photoscount=retrievephotos(set,0,1,"medium")
gp.addmessage("start to retrieving photos...")
getphotolist(set,0,photoscount,"medium")
gp.addmessage("retrieving photos completed")
cur=gp.InsertCursor(fc)
descfc=gp.Describe(fc)
print "begin insert features..."
gp.addmessage("begin insert features...")
for photo in lphotos:
row=cur.NewRow()
pnt=gp.CreateObject("POINT")
pnt.x=photo["longitude"]
pnt.y=photo["latitude"]
row.Setvalue(descfc.shapefieldname,pnt)
row.Setvalue("owner",photo["owner_name"])
row.Setvalue("title",photo["photo_title"])http://www.blogger.com/post-edit.g?blogID=21848155&postID=4114083803361112294#
row.Setvalue("photourl",photo["photo_file_url"])
cur.InsertRow(row)
print "insert features completed"
gp.addmessage("insert features completed")
del cur,descfc

        好啦,Python部分结束,添加到ArcMap中,便可成为一个Script Tool,与他人分享了。运行界面:

        运行结果:

        似乎不明显,那就利用空间分析中的Point Density工具抽象一下位置分布信息:

        也可以利用Model,将两部分连接起来,做成一个工具来用:

        有了地理数据,就可以继续在其他平台下进行处理和展示了:

最后总结一下标题:通过Python,不需要一个stand-alone的程序,就可以快速试验一个想法。
btw:附上最近google map tile的获取方法:

public override string GetTileUrl(int level, int row, int col)
{
int servernum = (col + 2 * row) % 4;
string sec2 = "&s="; // after &zoom=...
string secword = "Galileo";
int seclen = ((col * 3) + row) % 8; // corrected from int seclen = ( col + 3 * row ) % 8;
sec2 += secword.Substring(0, seclen);

//google maps map:
//http://mt1.google.com/vt/lyrs=m@114&hl=zh-CN&x=13&y=5&z=4&s=Gali
//http://mt1.google.com/vt/lyrs=m@114&hl=zh-CN&x=13&y=6&z=4&s=Galil
//http://mt1.google.com/vt/lyrs=m@114&hl=zh-CN&x=13&y=8&z=4&s=Galileo
string baseUrl = "http://mt" + servernum.ToString() + ".google.com/vt/lyrs=m@114&hl=zh-CN&x=";
string url = baseUrl + col.ToString() + "&y=" + row.ToString() + "&z=" + level.ToString() + sec2;
return url;

////google maps satallite
////http://khm1.google.com/kh/v=49&x=3&y=5&z=4&s=Galile
//string baseUrl = "http://khm" + servernum.ToString() + ".google.com/kh/v=49&x=";
//string url = baseUrl + col.ToString() + "&y=" + row.ToString() + "&z=" + level.ToString() + sec2;
//return url;
}

2009年11月19日星期四

哥斯拉拉拉拉拉拉

        为了弥补前两天看《2012doomsday》造成的精神损失,今天又找了同学推荐的《哥斯拉》来看,看的我好开心!从头笑到尾…《阿波罗13》《独立日》《哥斯拉》简直是一个模子里刻出来的,结局都是众人鼓掌,拥抱,一点悬念也没有。跟所有的动作片一样,开始好人很厉害,遇到一个boss,被打,爆发然后小宇宙,灭掉boss,或附加营救美女。我总想有这么一部影片,开始好人很厉害,遇到一个boss,被打,然后小宇宙没能爆发,壮烈牺牲,或附加美女被擒…也许这样的电影更能教育人们消灭坏人的重要性,或者大家都努力做个好人也不一定呢。

2009年11月17日星期二

ArcGIS API for Silverlight/WPF 1.1版本发布

        地址:http://resources.esri.com/help/9.3/arcgisserver/apis/silverlight/help/Whats_New.htm
        里面有很多重要变化,能够从各方面提高大家的工作效率:
  • 开发环境必须是Silverlight 3,Silverlight 2不再支持。这是因为sl3版本中多了很多重要的功能,arcgis api for Silverlight需要利用它们。包括behaviors,三维,element binding等。
  • 下载的API变成了可执行的安装包。之前版本的API都是大包的dll,自己引用就行,1.1版本需要在vs和blend里面进行配置,使得我们的开发工作变得更容易,比如在blend中对ArcGIS的控件进行拖拽操作。
  • 支持在Blend 3中进行可视化设计。至少大家不需要为地图等控件的布局头疼了。详见:Working with Expression Blend。
  • Silverlight程序模板。安装1.1版本API后,会自动在VS2008和Blend 3的新建工程中各创建一个模板程序,方便大家的开发工作。
  • 新的ESRI.ArcGIS.Client.Behaviors.dll库,充分利用Silverlight 3的Behavior特性。封装一些常用的鼠标操作,降低开发难度和工作量。具体请参见在线例子。
  • Toolkit命名空间中新的FeatureDataGrid和MapProgressBar控件。见其名,知其意。对于要展示图层属性表的开发者来说,前者再合适不过了。具体可参见在线例子。
  • ArcGISDynamicMapServiceLayer直接返回MIME数据,而不是生成图片的url;且输出支持PNG8,PNG24,PNG32,JPEG4中格式。这样就省去了一步获取生成图片的请求,提高效率。
  • 图层加载失败的时候自动抛出异常。Layer的InitializationFailed事件不再沉默,估计是简单的问题困扰了太多人,这算是比较贴心的一个改变。
  • ArcGISImageServiceLayer输出支持PNG8,PNG24,JPEG3中格式。
  • 全屏视图下支持鼠标滚轮操作。
  • FlareClusterer属性可在runtime时用代码进行设置;MapTip控件可设置消失的延迟时间。

        此外,还新上线一个Symbol Gallery,里面定义了很多漂亮有用的symbol,并带有xaml代码,大家可以拿来即用。

2009年11月15日星期日

豆瓣里的直角三角形

        为了证明自己还没有out,今天找了《2012》来看,结果越看越难看,上豆瓣看评论,发现自己搞错了,看的是《2012 Doomsday》。居然还有这样两个电影名。。。后者在豆瓣上的评分是个直角在下的直角三角形,这个三角形表达的含义就是:差到家了。
        浪费了1个多小时的时间,要不然我会用这些时间来玩《植物大战僵尸》。

2009年11月8日星期日

地理信息的另一种表达方式--Cartogram地图

        先来看一则旧闻:
英科学家绘制新世界人口地图 中印两国最突出
        下面是几张新闻里的地图:


        第一张图中可以看出,目前时间上人口最多的中国和印度被表达的很夸张,但这种图形上的夸张恰恰很好的表达了两国人口与其他国家人口数量的相对关系。这种地图被称为Cartogram
The geometry or space of the map is distorted in order to convey the information of this alternate variable.

        为了要强烈表达地图中某种属性信息,而将图形进行一些扭曲,而扭曲的重要原则就是不改变原图形的拓扑关系。
        那么在ArcGIS Desktop中如何做出这种地图呢?请到ArcScripts网站下载这个免费的工具,安装好后它会出现在ToolBox中。它由密歇根大学的 Mark Newman和Michael Gastner开发完成。压缩包中已经包含了详细的使用说明和实例数据,下面是自带数据做出的一张2007年世界各国GDP总量地图:

        再次提醒,当你想突出地图上的某个属性信息而制作一张令人印象深刻的专题图时,记得试试这个工具。别忘了它适用矢量、栅格两种数据,而且可以做出经过两个或多个属性变量影响的cartogram地图。
        利用ArcGIS Desktop和这个工具,我们不是也作出了英国科学家才做出的地图吗:)

2009年11月7日星期六

ArcGIS中的线性参考/动态分段技术(三)几个Linear Referencing应用场景在AGS中的实现

        现在来实现上面提到的3个功能。
1、对于某条公路上的一点进行Identify操作,要求返回该点在公路上的桩号值:
        以Silverlight API为例。为了在服务器端使用ArcObjects,在Asp.net工程中添加一个名为LinearRef的Silverlight-Enabled WCF Service,在LinearRef.svc.cs文件中添加以下代码:

namespace RoadDycSeg.Web.WCF
{
[ServiceContract(Namespace = "")]
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
public class LinearRef
{
public IRouteLocator2 pRtLocator = null;
ESRI.ArcGIS.Server.IServerObjectManager pSOM = null;
public ESRI.ArcGIS.Server.IServerContext pServerContext = null;

public LinearRef()
{
ESRI.ArcGIS.ADF.Identity identity = new ESRI.ArcGIS.ADF.Identity("ArcGISWebServices", "yourpassword", "");
ESRI.ArcGIS.ADF.Connection.AGS.AGSServerConnection agsconn = new ESRI.ArcGIS.ADF.Connection.AGS.AGSServerConnection("localhost", identity);
agsconn.Connect();
pSOM = agsconn.ServerObjectManager;
pServerContext = pSOM.CreateServerContext("shaanxi", "MapServer");
ESRI.ArcGIS.Carto.IMapServer2 pMapServer = pServerContext.ServerObject as ESRI.ArcGIS.Carto.IMapServer2;
ESRI.ArcGIS.Carto.IMapServerObjects2 pMapServerObjects = pMapServer as ESRI.ArcGIS.Carto.IMapServerObjects2;
ESRI.ArcGIS.Carto.IMap pMap = pMapServerObjects.get_Map(pMapServer.DefaultMapName);
IFeatureClass pFC = (pMap.get_Layer(2) as ESRI.ArcGIS.Carto.IFeatureLayer).FeatureClass;

//create the RouteLocator
IDataset dS = (IDataset)pFC; // A polylineM feature class.
IName name = dS.FullName;
IRouteLocatorName rtLocatorName = pServerContext.CreateObject("esriLocation.RouteMeasureLocatorName") as IRouteLocatorName;
rtLocatorName.RouteFeatureClassName = name;
rtLocatorName.RouteIDFieldName = "道路编码";
rtLocatorName.RouteMeasureUnit = esriUnits.esriUnknownUnits;
name = (IName)rtLocatorName;
pRtLocator = (IRouteLocator2)name.Open();

pServerContext.ReleaseContext();
}

// Add more operations here and mark them with [OperationContract]

[OperationContract]
///
/// used by the client Identify operation and return the result
///

/// the x coords of the mouseclick in map units
/// the y coords of the mouseclick in map units
/// the map distance of 1 pixel
/// a identifyresult class defined in Classes.cs. If no result, mvalue=x=y=-1
public IdentifyResult Identify(double mapX, double mapY, double resolution)
{
pServerContext = pSOM.CreateServerContext("shaanxi", "MapServer");

IdentifyResult ir = new IdentifyResult("", -1, -1, -1);

IPoint pPoint = pServerContext.CreateObject("esriGeometry.Point") as IPoint;
pPoint.PutCoords(mapX, mapY);
IEnvelope pEnvelope = pPoint.Envelope;
pEnvelope.Expand(10 * resolution, 10 * resolution, false);

IEnumRouteIdentifyResult pEnumResult = pRtLocator.Identify(pEnvelope, "");
pEnumResult.Reset();
//only get the first result
if (pEnumResult.Count > 0)
{
IRouteLocation pRL = null;
IFeature pF = null;
pEnumResult.Next(out pRL, out pF);
IRouteMeasurePointLocation pRMPL = pRL as IRouteMeasurePointLocation;

//retrieve the location geometry by calling LOCATE method
IRouteLocation routeLocation = pServerContext.CreateObject("esriLocation.RouteMeasurePointLocation") as IRouteLocation;
routeLocation.MeasureUnit = esriUnits.esriUnknownUnits;
routeLocation.RouteID = pRL.RouteID;
routeLocation.LateralOffset = 0;
IRouteMeasurePointLocation rMPointLoc = (IRouteMeasurePointLocation)routeLocation;
rMPointLoc.Measure = pRMPL.Measure;

IGeometry geom;
esriLocatingError locError;
pRtLocator.Locate((IRouteLocation)rMPointLoc, out geom, out locError);

ir.RouteID = pRL.RouteID.ToString();
ir.MValue = pRMPL.Measure;
ir.X = (geom as IPointCollection5).get_Point(0).X;
ir.Y = (geom as IPointCollection5).get_Point(0).Y;
}

pServerContext.ReleaseContext();

return ir;
}

        其中,IdentifyResult是自定义的一个类,用来存储查询的结果:
namespace RoadDycSeg.Web.WCF
{
///
/// the result of Identify operation
///

[DataContract]
public class IdentifyResult
{
[DataMember]
public string RouteID {get;set;}
[DataMember]
public double MValue {get;set;}
[DataMember]
public double X {get;set;}
[DataMember]
public double Y {get;set;}

public IdentifyResult(string routeid, double mvalue, double x, double y)
{
RouteID = routeid;
MValue = mvalue;
X = x;
Y = y;
}
}
}

        Identify方法需要传入3个参数,一个点的地理坐标以及当前地图的Resolution,后者用来生成一个10像素的缓冲区,方便用户的点击操作;其中利用IRouteLocator2.Identify方法获得M值,据此利用 IRouteLocator2.Locate方法获得精确落在公路上的点的Geometry,用以显示在客户端;如果用户点击的点距离公路较远,则IRouteLocator2.Identify结果为空,返回的IdentifyResult中M值为-1。
        在Silverlight工程中,Add Service Reference,找到刚才的LinearRef服务,取名为LRService。
        客户端中,新建一个COperation类,实现业务操作。
public class COperation
{
private LRService.LinearRefClient LR = null;
private GraphicsLayer glayer = null;
private bool isBusy = false;//indicate whether there is an operation is performing
private Map Map1 = null;
public COperation(Map map)
{
Map1=map;
glayer = Map1.Layers["glayer"] as GraphicsLayer;
//initialize the LR object
LR = new RoadDycSeg.LRService.LinearRefClient();
LR.IdentifyCompleted += new EventHandler(LR_IdentifyCompleted);
}
private void LR_IdentifyCompleted(object sender, LRService.IdentifyCompletedEventArgs e)
{
LRService.IdentifyResult ir = e.Result;
//add a point graphic to the map with attributes displaying by the maptip, maptip defined in mainpage.xaml
if (ir.X > 0)//the result exists
{
Graphic g = new Graphic()
{
Geometry = new MapPoint(ir.X, ir.Y),
Symbol = Application.Current.Resources["strobeSymbol"] as Symbol,
};
g.Attributes.Add("RouteID", ir.RouteID);
g.Attributes.Add("桩号", ir.MValue);
g.Attributes.Add("X坐标", ir.X);
g.Attributes.Add("Y坐标", ir.Y);
glayer.Graphics.Clear();
glayer.Graphics.Add(g);
}
isBusy = false;
}

private void Map1_Identify(object sender, ESRI.ArcGIS.Client.Map.MouseEventArgs e)
{
if (!isBusy)
{
LR.IdentifyAsync(e.MapPoint.X, e.MapPoint.Y, Map1.Resolution);
isBusy = true;
}
}

        在需要使用Identify功能时,将Map控件的Click事件绑定到Map1_Identify即可。结果如图:

2、输入起始和终止桩号,要求将其间的路段显示在地图上:
        要解决这个问题,关键是根据两个M值,求的其间一段公路的Geometry。利用的仍然是IRouteLocator2.Locate方法,其中的第一个参数routeLocation,传入的是用IRouteMeasureLineLocation接口定义的“线性位置”,具体可参考帮助文档。
        仍然是在LinearRef.svc.cs文件的LinearRef类中,添加以下方法:
[OperationContract(Name = "RetrieveRoutePortionGeometry")]
///
/// used by the client,such as QueryPortion operation, to retrieve the geometry of the route portion
///

/// the route id which the portion is part of
/// fromMvalue
/// toMvalue
/// a json string with specific format,such as [{"x":1111,"y":1111},{"x":2222,"y":2222},{"x":3333,"y":3333}.....]
public string RetrieveRoutePortionGeometry(string routeid, double fromMValue, double toMValue)
{
pServerContext = pSOM.CreateServerContext("shaanxi", "MapServer");
IRouteLocation routeLoc = pServerContext.CreateObject("esriLocation.RouteMeasureLineLocation") as IRouteLocation;
routeLoc.MeasureUnit = esriUnits.esriUnknownUnits;
routeLoc.RouteID = routeid;
routeLoc.LateralOffset = 0;
IRouteMeasureLineLocation rMLineLoc = (IRouteMeasureLineLocation)routeLoc;
rMLineLoc.FromMeasure = fromMValue;
rMLineLoc.ToMeasure = toMValue;

IGeometry geom;
esriLocatingError locError;
pRtLocator.Locate((IRouteLocation)rMLineLoc, out geom, out locError);

pServerContext.ReleaseContext();

//return the routeportion's vertices
StringBuilder sb = new StringBuilder("[");
IPolyline pLine = geom as IPolyline;
IPointCollection pPC = pLine as IPointCollection;
//json format such as:
//[{"x":1111,"y":1111},{"x":2222,"y":2222},{"x":3333,"y":3333}.....]
for (int i = 0; i < pPC.PointCount; i++)
{
sb.Append("{\"x\":");
sb.Append(pPC.get_Point(i).X.ToString());
sb.Append(",\"y\":");
sb.Append(pPC.get_Point(i).Y.ToString());
sb.Append("},");
}
//remove the last ","
sb.Remove(sb.Length - 1, 1);
sb.Append("]");
return sb.ToString();
}


        由于在服务器端的AO操作,产生的结果是ESRI.ArcGIS.Geometry.IGeometry类型,而客户端需要的则是ESRI.ArcGIS.Client.Geometry.Geometry类型,所以需要对结果序列化/反序列化,这里采用JSON字符串处理。客户端处理结果的函数仍然放在COperation.cs文件中,如下:
private void LR_RetrieveRoutePortionGeometryCompleted(object sender,LRService.RetrieveRoutePortionGeometryCompletedEventArgs e)
{
//create a esri.arcgis.client.geometry.polyline from the coords of points contained in the json string
string jsonpoints = e.Result;
JsonArray jsonarrays = (JsonArray)JsonArray.Load(new System.IO.StringReader(jsonpoints));
ESRI.ArcGIS.Client.Geometry.Polyline line = new ESRI.ArcGIS.Client.Geometry.Polyline()
{
SpatialReference = Map1.SpatialReference,
};
ESRI.ArcGIS.Client.Geometry.PointCollection pc = new ESRI.ArcGIS.Client.Geometry.PointCollection();
foreach (JsonObject jo in jsonarrays)
{
pc.Add(new ESRI.ArcGIS.Client.Geometry.MapPoint(jo["x"], jo["y"]));
}
line.Paths.Add(pc);

//add a graphic
Graphic g = new Graphic()
{
Geometry = line,
Symbol = Application.Current.Resources["LineSymbol"] as SimpleLineSymbol,
};
glayer.Graphics.Add(g);
}

        Silverlight中提供了System.Json库,可以非常方便的对JSON字符串进行解析。结果如图:

3、类似GoogleMap的交通流量地图:
        首先看一下Google的交通流量地图:

        可以看出,之所以能产生不同颜色段,需要的数据有3个:该段的起始和终止M值,该段的车流量。假设我们获取的业务数据表(EventTable)如下:

        联想到上面第二个应用场景,我们便想可以获取所有交通数据后,用循环的办法展示出交通流量地图。但这里有两个问题需要思考:1、即使使用IRouteLocator2.LocateRow方法,服务器端根据起止M值解析出所有的分段图形需要一次循环,客户端将这些图形显示出来还需要一次循环,这样是否合理?2、由于结果显示在客户端,不同用户发出请求时,都将重复上面两个循环,如何改进?
        解决问题一:在第二节的Linear Referencing实现原理中,提到了RouteEventSource类,可以将它看作是把Route FeatureClass和EventTable组合在一起的结果,而该类继承自FeatureClass,可当作一个FeatureClass来用。在ArcMap中观察生成后(利用Make Route Event Layer工具)的RouteEventSource层:

        不仅包含了EventTable中的事件,还有了我们想要的Shape字段(可取出Geometry)。其实Shape字段中的内容,是利用Dynamic Segmentation技术动态计算出来的。看其图层属性便知,它是在内存中动态生成的:

        有了FeatureClass,我们便可以对整个图层进行一次渲染,避免服务器端和客户端的两次循环了。这里要注意,FeatureClass是动态生成的,图层便是动态添加,在地图服务的REST接口中无法暴露出来,也就不能利用客户端API的渲染技术了。
        解决问题二:利用SOA的思想,将交通流量图做成一个Traffic服务,不同用户请求时动态添加该服务即可。想想这个服务应该具有的特性:不同时段的交通图,需要动态生成;如果交通图已经生成,则无需重新生成,显示即可。所以我们采取池化方式服务(pooled-service)的特性,刚好解决这一问题。
        这个场景我们用Web ADF来实现(客户端API与前两个场景相同)。
        首先准备两个Map Service:底图服务:shaanxi,交通流量图服务:ShaanxiTraffic。
        后者在ArcMap中打开是这样的:

        ShaanxiTraffic服务除了动态生成的交通流量图外,不需要在显示任何信息,所以发布时是空白的;因为动态生成的RouteEventSource将作为图层插入到这个地图服务中,所以Route FeatureClass必须也在这个mxd中才行。
        Default.aspx页面中放置一个MapResourceManager控件、一个Map控件、一个CallbackButton按钮和一个GridView控件。先看一下结果:

        点击按钮后:

        看一下Default.aspx.cs中按钮的点击事件:
protected void CallbackButton1_Clicked(object sender, EventArgs args)
{
//先判断Traffic服务中是否已经产生结果
if (hasTrafficResult())//Traffic服务中已经有结果
{
//将结果插入地图
InsertTrafficService();
}
else//服务中还没有结果
{
//先生成交通流量结果
MakeResult();
//将结果插入地图
InsertTrafficService();
}
//显示RouteEventSource的属性表
DisplayAttributeTable();
}

        思路都在代码中。依次来看几个函数:
///
/// 判断池化的Traffic服务中,获取的ServerContext是否已经产生结果
///

/// true or false
private bool hasTrafficResult()
{
ESRI.ArcGIS.ADF.Identity identity = new ESRI.ArcGIS.ADF.Identity("ArcGISWebServices", "yourpassword", "");
ESRI.ArcGIS.ADF.Connection.AGS.AGSServerConnection agsconn = new ESRI.ArcGIS.ADF.Connection.AGS.AGSServerConnection("machinename", identity);
agsconn.Connect();
pSOM = agsconn.ServerObjectManager;
ESRI.ArcGIS.Server.IServerContext pServerContext = pSOM.CreateServerContext("ShaanxiTraffic", "MapServer");
ESRI.ArcGIS.Carto.IMapServer2 pMapServer = pServerContext.ServerObject as ESRI.ArcGIS.Carto.IMapServer2;
ESRI.ArcGIS.Carto.IMapServerObjects2 pMapServerObjects = pMapServer as ESRI.ArcGIS.Carto.IMapServerObjects2;
ESRI.ArcGIS.Carto.IMap pMap = pMapServerObjects.get_Map(pMapServer.DefaultMapName);
pServerContext.ReleaseContext();
return pMap.LayerCount == 1 ? false : true;
}

        对于ShaanxiTraffic服务,在获得的Server Object中,查看当前Map的图层数(默认有一个隐藏的Road图层),若为1则还未产生结果。
        下面是动态添加服务的函数,注意要设置背景透明,以便能够看到底图服务:
///
/// 动态添加服务
///

private void InsertTrafficService()
{
if (MapResourceManager1.ResourceItems.Count==1)//若已插入交通流量服务,则不做动作
{
MapResourceItem mapResourceItem = new MapResourceItem();
GISResourceItemDefinition definition = new GISResourceItemDefinition();
mapResourceItem.Name = "Traffic";
definition.ResourceDefinition = "Layers@ShaanxiTraffic";
definition.DataSourceDefinition = "machinename";
definition.DataSourceType = "ArcGIS Server Local";
mapResourceItem.Parent = MapResourceManager1;
mapResourceItem.Definition = definition;
ESRI.ArcGIS.ADF.Web.DisplaySettings displaysettings = new ESRI.ArcGIS.ADF.Web.DisplaySettings();
displaysettings.Transparency = 0.0F;
displaysettings.Visible = true;
ESRI.ArcGIS.ADF.Web.ImageDescriptor imagedescriptor = new ESRI.ArcGIS.ADF.Web.ImageDescriptor();
imagedescriptor.TransparentBackground = true;
imagedescriptor.TransparentColor = System.Drawing.Color.White;
displaysettings.ImageDescriptor = imagedescriptor;
mapResourceItem.DisplaySettings = displaysettings;

MapResourceManager1.ResourceItems.Insert(0, mapResourceItem);
MapResourceManager1.CreateResource(mapResourceItem);

if (Map1.ImageBlendingMode == ImageBlendingMode.WebTier)
{
Map1.Refresh();
}
else
{
Map1.RefreshResource("Traffic");
}

CallbackButton1.CallbackResults.CopyFrom(Map1.CallbackResults);
}
}

        MakeResult函数利用ESRI.ArcGIS.Location库来生成交通流量图。由于Traffic服务是池化服务,当第一个用户请求,生成交通流量图后,该实例(servercontext)会返回服务器上而不被销毁,所以下一个用户再次请求时可以直接显示。如果要实现实时的交通流量图,定时在服务器端重生servercontext,并读取当前时段的EventTable即可。
///
/// 生成交通流量的结果
///

private void MakeResult()
{
ESRI.ArcGIS.Server.IServerContext pServerContextTraffic = pSOM.CreateServerContext("ShaanxiTraffic", "MapServer");
ESRI.ArcGIS.Carto.IMapServer2 pMapServer = pServerContextTraffic.ServerObject as ESRI.ArcGIS.Carto.IMapServer2;
ESRI.ArcGIS.Carto.IMapServerObjects2 pMapServerObjects = pMapServer as ESRI.ArcGIS.Carto.IMapServerObjects2;
ESRI.ArcGIS.Carto.IMap pMap = pMapServerObjects.get_Map(pMapServer.DefaultMapName);
//创建RouteLocator
IFeatureClass pFC = (GetLayerByName("Road",pMap) as IFeatureLayer).FeatureClass;
IDataset dS = (IDataset)pFC; // A polylineM feature class.
IName name = dS.FullName;
IRouteLocatorName rtLocatorName = pServerContextTraffic.CreateObject("esriLocation.RouteMeasureLocatorName") as IRouteLocatorName;
rtLocatorName.RouteFeatureClassName = name;
rtLocatorName.RouteIDFieldName = "道路编码";
rtLocatorName.RouteMeasureUnit = esriUnits.esriUnknownUnits;
name = (IName)rtLocatorName;
IRouteLocator2 pRtLocator = (IRouteLocator2)name.Open();

//创建RouteEventProperties,为RouteEventSource做准备
IRouteEventProperties2 rtProp = pServerContextTraffic.CreateObject("esriLocation.RouteMeasureLineProperties") as IRouteEventProperties2;
rtProp.AddErrorField = true;
rtProp.ErrorFieldName = "LOC_ERROR";
rtProp.EventMeasureUnit = esriUnits.esriUnknownUnits;
rtProp.EventRouteIDFieldName = "RouteID";
IRouteMeasureLineProperties rMLineProp = (IRouteMeasureLineProperties)rtProp;
rMLineProp.FromMeasureFieldName = "FromM";
rMLineProp.ToMeasureFieldName = "ToM";

//创建RouteEventSource
ITable eventTable = (pFC.FeatureDataset.Workspace as IFeatureWorkspace).OpenTable("lineEventsTable");
IDataset ds = (IDataset)eventTable;
IName name2 = ds.FullName;
IRouteEventSourceName rtEvtSrcName = pServerContextTraffic.CreateObject("esriLocation.RouteEventSourceName") as IRouteEventSourceName;
rtEvtSrcName.EventTableName = name2;
rtEvtSrcName.EventProperties = (IRouteEventProperties)rMLineProp;
rtEvtSrcName.RouteLocatorName = rtLocatorName;
name2 = (IName)rtEvtSrcName;
IRouteEventSource rtEvtSrc = (IRouteEventSource)name2.Open();
//转换到FeatureClass
pFC = rtEvtSrc as IFeatureClass;

//将RouteEventSource添加到地图中,并进行唯一值渲染
IUniqueValueRenderer pUVR = pServerContextTraffic.CreateObject("esriCarto.UniqueValueRenderer") as IUniqueValueRenderer;
pUVR.FieldCount = 1;
pUVR.set_Field(0, "Vehicles");
pUVR.UseDefaultSymbol = false;
IFeatureCursor pFeatureCursor = pFC.Search(null, false);
IFeature pFeature = pFeatureCursor.NextFeature();
int ifiledindex = pFC.Fields.FindField("Vehicles");//EventTable中的车流量字段
while (pFeature != null)
{
ISimpleLineSymbol pSymbol = pServerContextTraffic.CreateObject("esriDisplay.SimpleLineSymbol") as ISimpleLineSymbol;
double value = double.Parse(pFeature.get_Value(ifiledindex).ToString());
if (value <= 20)
pSymbol.Color = ESRI.ArcGIS.ADF.Converter.ToRGBColor(pServerContextTraffic, System.Drawing.Color.FromArgb(71, 196, 69)) as IColor;
else if (value > 20 && value <= 50)
pSymbol.Color = ESRI.ArcGIS.ADF.Converter.ToRGBColor(pServerContextTraffic, System.Drawing.Color.FromArgb(255, 255, 0)) as IColor;
else if (value > 50 && value <= 100)
pSymbol.Color = ESRI.ArcGIS.ADF.Converter.ToRGBColor(pServerContextTraffic, System.Drawing.Color.FromArgb(201, 59, 46)) as IColor;
else
pSymbol.Color = ESRI.ArcGIS.ADF.Converter.ToRGBColor(pServerContextTraffic, System.Drawing.Color.FromArgb(250, 52, 17)) as IColor;
pSymbol.Width = 5;
pUVR.AddValue(pFeature.get_Value(ifiledindex).ToString(), "Vehicles", pSymbol as ISymbol);
pFeature = pFeatureCursor.NextFeature();
}
IFeatureLayer pFL = pServerContextTraffic.CreateObject("esriCarto.FeatureLayer") as IFeatureLayer;
pFL.Name = "RouteEventSource";
pFL.FeatureClass = pFC;
(pFL as IGeoFeatureLayer).Renderer = pUVR as IFeatureRenderer;

pMap.AddLayer(pFL as ILayer);//to the 0 index
pMapServerObjects.RefreshServerObjects();
pServerContextTraffic.ReleaseContext();
}

        一点注意,在使用IServerContext接口时要及时进行释放。
        思考一下:如何通过Geoprocessing Service来实现“交通流量图”?

2009年11月6日星期五

利用免费的地图来解决实际问题

        Google Map的API虽然免费,好用,但终究需要那么一些Javascript基础;ESRI最近推出的Mapping for Everyone使用起来就更简便,内容也更丰富,能够让大家轻松地在自己网页里嵌入一张可互动的地图,比如这样就可以查看美国09年的失业率统计:

        它能够帮助我们做什么呢?这就有个例子,这位老兄正利用一张地图,劝说他的好友卖掉房子,准备搬家呢:)

2009年11月4日星期三

ArcGIS中的线性参考/动态分段技术(二):Linear Referencing在ArcGIS Server中的实现原理

        首先来思考几个Linear Referencing的应用场景(后面会对这些场景一一实现):
        1、对于某条公路上的一点进行Identify操作,要求返回该点处在公路上的里程值;
        2、输入起始和终止桩号,要求将其间的路段显示在地图上;
        3、类似GoogleMap的交通流量地图。
        要将这些功能在ArcGIS Server中“落地”,首先决定采用哪种开发方式:客户端API还是Web ADF?两者皆可。但是稍加思考,两种方式的API中都没有提供有关Linear Referencing的解决办法。所以,Linear Referencing在ArcGIS Server中的实现原理还是得依靠ArcObjects了。
        ArcObjects中,Linear Referencing的功能在ESRI.ArcGIS.Location库中,请见:http://resources.esri.com/help/9.3/ArcGISEngine/dotnet/10745641-b5e3-4600-979f-cdf9d2bbc7ce.htm#About%20linear%20referencing
        为了实现上述功能,其中比较关键的有两个类:RouteLocator和RouteEventSource。RouteLocator的IRouteLocator2接口有Identify和Locate两个方法,前者可根据地理坐标识别出Route上的位置(M值);后者可根据M值来定位出Route上相应的Geometry。如果已有EventTable,需要对上面的每个Event进行定位,除了重复使用IRouteLocator2.Locate方法(或者LocateRow方法)外,也可通过构建RouteEventSource来实现。它是一种特殊的FeatureClass(继承自FeatureClass),其中的每个Feature代表了EventTable中的一个Event,而这个Feature的Shape字段内容则是根据EventTable和Route FeatureClass,利用Dynamic Segmentation技术实时计算出来的。
        关于如何获得可用的IRouteLocator2接口和RouteEventSource,文档中已经讲的很清楚;另外ESRI.ArcGIS.Location中还提供了实现ArcMap中Linear Referencing里所有功能的接口,有兴趣的朋友可以仔细阅读。

2009年11月3日星期二

ArcGIS中的线性参考/动态分段技术(一):Linear Referencing背景

Linear Referencing背景
        定位:介绍Linear Referencing背景;在ArcMap中关于Linear Referencing的结构和功能;以及ArcGIS Server中几个应用场景的实现。
        注:有关更详细的操作可查看在线帮助,或搜索论坛。
什么是Linear Referencing
        Linear Referencing(下文引用为LR)翻译过来是线性参考,在公路,管网等行业的GIS应用中时常提到。LR是一种利用沿着可测量的线要素的相对方位来存储地理位置的方法。比如下图中:

        下面线的长度一次标为0,10,20,30,40……,而沿着这条线,我们看到上面有:
  • 一个位于坐标12处的点;
  • 一个位于坐标10东侧4个坐标的点;
  • 一段起始坐标分别为18和26的线段;
  • 一段起始坐标为28,长度为12的线段。

        为什么要用到LR技术?主要有两个原因:1、很多事件,像上边的例子一样,是通过沿着(曲)线的相对位置来记录的;2、要显示一条线上的多个属性集合时,由于各个属性在(曲)线上所对应的位置不同,同一数据源如果不做处理,很难达到要求。使用LR技术可解决此问题。
        以公路方面的一个应用场景为例说明。我们要显示一条公路的4种不同属性:道路管辖情况、路面材料、路段限速情况和路况,假设该公路长100公里:

1、前40公里为交警2大队管辖,后60公里为交警4大队管辖;
2、30至70公里为水泥路面,其余为沥青路面;
3、0-20公里的路段限速45km/h,20-40公里的路段限速35km/h,40-70公里路段限速45km/h,70-100公里路段限速55km/h;
4、0-20公里路况一般,20-40公里路况很好,40-60公里路况很差,60-100公里路况很好。
        属于同一数据源对应多个属性(且属沿线分布)的情况。如果不使用LR技术,那么需要4个公路图层,每个图层的公路根据属性分成长短不同的段落(Feature),才能够将这些属性展示出来;而是用了LR技术后,只需要一个公路数据(Feature数量不限),和四个事件表即可在不改变实际公路数据的情况下,按要求显示上述四种属性。
        Dynamic Segmentation:动态分段,属于LR采用的一种技术(一般应称之为线性参考问题中的动态分段技术)。是根据属性表中存储的相对位置信息,以及相应的线性数据,动态计算出线性数据上相对位置所对应的实际地理坐标的过程。动态分段正是因为表达不同属性时,不用去分割实际的地理数据,而是动态计算出该属性对应的地理位置而得名。
ArcGIS中Linear Referencing的实现原理
        ArcGIS中实现Linear Referencing主要通过以下两种数据结构:
1、Route FeatureClass
2、Event Table
        通过Dynamic Segmentation技术,Event Table中不同位置的Event就定位到了Route FeatureClass下对应的Line Feature上。
Route FeatureClass
        实际上是拥有两个特殊字段的Polyline FeatureClass:
1、必须包含有M(Measure)值的Shape字段。拥有M值的Shape字段,不但能存储x,y(,z)坐标,还能够多存储一个M数值;
2、必须包含有一个标识线段ID的字段,可以是Number或者Text类型。
        除了上述两个字段要求,还可以有其他字段。

        Route FeatureClass不同于普通的Polyline FeatureClass,是因为它具有一个测量系统,而这个测量系统的原理就是,通过存储在Feature的Vertice中的M值以及该Feature实际的ShapeLength,来动态插值出线上每一点的相对位置。比如一条公路数字化时,有两个节点(Vertice),分别表示该公路的起始桩号0公里和结束桩号100公里,该Feature存储在WGS1984坐标系中,ShapeLength为2.00,那么该公路上40公里处的位置,就应该位于图上该Feature的40%的位置,也就是ShapeLength为0.80的位置。
        而ArcGIS中,不要求每个节点都必须有M值。对于没有M值的节点其M值会标记为NaN(not a number):

        此外在不违反逻辑错误的情况下,M值可以随意设置。比如上图左边的图形,0-10段的实际ShapeLength看起来比10-20段的ShapeLength明显要长,也可以将上述四个节点的M值随意地设置为0,3,7,30。但是如果设置为0,10,5,30的话就产生了逻辑错误(route measure anomalies),ArcMap中会将其突出显示,提醒用户使用工具修改。
Event Table
        实际上是拥有2-3个特殊字段的表(ArcGIS支持的表均可,甚至可以是有特定格式的text文件):
1、类似Route FeatureClass中RouteID的一个字段。用来存储与Route FeatureClass中哪个线Feature对应。可以是Number或Text类型;
2、根据Event类型不同而必须的1-2个字段:
  • Point Event:比如108国道1900公里处发生了交通事故这个Event,需要一个Number类型的字段来记录1900这个相对位置;
  • Line Event:比如108国道1850到1950公里段的路况很差这个Event,需要两个Number类型的字段来分别记录1850、1950这两个起止位置。

        除了上述两个字段要求,还可以有其他字段。
        Event Table中一条记录就对应了需要在线上定位的一个点或一段距离,可以用来标识事件或属性等。下图描述了Dynamic Segmentation技术利用Route FeatureClass和Event Table产生的结果:

ArcGIS桌面中对Linear Referencing的实现
        ArcGIS桌面中不仅提供了专门的Linear Referencing Toolbox,对于产生的结果图层,也能像普通图层一样进行各种操作,而针对Linear Referencing的方方面面也提供了非常细粒度的操作供用户调整。
  • 对Route FeatureClass的创建与修改。包括:通过Catalog或Tool或向导创建RouteFeatureClass;利用Tool从已有的Polyline FeatureClass创建Route FeatureClass;通过Tool或向导利用已有的Point FeatureClass来校准Route FeatureClass的M值(注:通过对RouteID字段建立索引,可提高Dynamic Segmentation的效率); 利用Route Editing Toolbar对其进行修改;
  • 将Route FeatureClass作为图层显示,并对其进行查询。包括:对Route进行Identify。查询出线上任意点的M值(Identify Route Locations工具需从Customize中调出);查找某条线上指定M值的位置(包括Point和Line两种),通过Find工具实现;自定义符号显示M值的异常。包括逻辑错误等,可通过Route图层的属性对话框实现;在地图上像刻度尺一样任意显示Route的M值(Hatch)。通过将定义Hatch Definition(可多个),将其组织在Hatch Class(可多个)下,并附加SQL Query和Range Scale操作,基本实现任意复杂的“道路标尺”,而“标尺”的Label也可进行Script的高级控制,最终还能将“标尺”转换为Graphics,或将“标尺”风格另存为Style。在Route图层的属性对话框中实现;
  • 通过Tool和Attribute Table,对Event Table进行创建和修改;
  • 通过Tool,利用Dynamic Segmentation技术将Event Table中的Event显示在Route上(可显示点、线、面),其结果作为一个图层添加到TOC中,可进行显示、查询和分析等操作;
  • 通过Tool或向导,对Route Event进行Overlay,Aggregate,Transform等操作。

        以上所有具体操作可参考桌面帮助中的内容,如果安装了ArcTutor,还会有Linear Referencing Tutorial的PDF可供学习。

2009年10月4日星期日

一个摄影记者的梦想

        其实应该是自己想成为一个摄影记者,但估计实现的可能性非常小的一个梦想。
        记者,从某种程度上来说与老师一样,是非常值得令人尊重的职业,有着非常崇高的职业道德和敬业精神。而摄影记者更是不易,他们不做采访也不做评论,只是将最真实的一面呈献给大众,他们需要把想要表达的东西凝固在那一瞬间;从另一个角度来说,他们也无需用文字表达,把思考的过程和空间都留给读者或者观众。
        下面就是一个名为Dan Chung的英国华裔摄影记者(《卫报》长派中国记者),用它的佳能5D Mark2,7D还有尼康D700拍摄的国庆阅兵短片。慢动作部分是用7D的720p,60帧/秒摄影功能完成,时间流动效果由其他两部机器完成。
        一个个的慢动作仿佛记录的不仅是现在,而且还有历史、思考和未来。以我外行的眼光来看,这正是一个合格并且优秀的摄影记者为什么会让人肃然起敬的原因。

2009年10月3日星期六

真实就是力量

        《我们俩》,本以为这个电影演的是90年代大学生租房住的事情,可看了之后才发现不是,讲的是亲情。亲情是很长时间的故事,电影里把它浓缩了;但都抓到了点子上,看了又让人能想起许多电影里没演的事情。很多年以后,可能爱情的片子已经无法让你悸动,但是关于亲情的片子,肯定会感人一生。
        makeing life look as good as cinema,其实大可不必刻意这样去做,因为真实的生活本来就很精彩。
        这部影片的导演是马俪文,很难想象她最后“沦落”到拍出《我叫刘跃进》(个人定义:疯狂的石头,我叫刘跃进,疯狂的赛车并成为疯狂三部曲)的地步;电影配乐是窦唯,这倒是很容易让人想到他的《觉是》。ps:丰富的风格vs执着的坚持?

2009年9月21日星期一

请爱我们的地球


        两年前看过《An Inconvenient Truth》,今天又看了《Home》,人类虽然还没有被自己逼到绝境上,但也到了“要悲观已经太迟”的地步了。
        即使人类如此万能,至今也没能在宇宙中找到一颗类似地球的行星;地球上一个物种的从无到有,需要经历的无数的轮回。人类能出现在今天的历史舞台上,实在已经是一件值得庆幸和感恩的事情了。
        而我们又是如何对待养育我们的地球呢?贪婪不绝地、拼命地从地球身上抽取石油;将肮脏的,自己闻了都嫌臭的垃圾导入蔚蓝的大海;出于炫富的心理,攀比着购买对于有些人来说可买可不买的汽车,在抱怨油价高、交通状况差的时候却从来没有考虑过因为排放尾气,自己又该为气候变暖,臭氧空洞承担多少责任?所有其它的生物也都理应与我们一样,拥有居住在这个星球上的权利,可我们已经因为自己的不负责任的行为,造成了数以千计物种的灭绝,打破了天然的,异常完美的生态平衡;今天,我们吃猪肉,买鸡蛋,喝牛奶,多么平常的事情,可站在它们的立场上呢?猪想,我生来就拥有拼命的长,然后宰杀的命运么?难道一天吃吃睡睡,晒晒太阳的日子不好么?鸡想,我的小宝宝还没出生,都已经被你们端到了餐桌上,这是怎么一回事情?难道带着自己的小宝宝,和爸爸玩玩老鸡捉小鸡的游戏不好么?牛想,我吃的是草,挤出的是奶,自己却连一口也喝不到,难道一天到晚散散步,闻闻稻香的日子不好么?已经有太多不应该发生的事情,被我们麻木地当成了理所当然。
        清晰记得小学的时候,有一年冬天看到第一场雪,我在本子上记下了11月14号的日子。而现在呢?小朋友们可能以为西安一年不下雪是很正常的了吧……有时候,思念雪要比思念童年更加忧伤。
        看《E.T.》的时候,当外星人抬头望着天空,眨着它那硕大的眼睛,激动的说出:“Home...”的一幕,眼眶不觉的湿润。希望我们人类不要沦落到这一天,在别的星球上,指着远方的地球,感伤的说出:“那曾是我们的家园”……

2009年9月16日星期三

抢先看:细数ArcMap 9.4中的九大亮点

        介绍:esri 09用户大会上的视频,其中详细介绍了于明年二季度即将发布的ArcGIS 9.4产品中,ArcMap 9.4的9大亮点。
        视频地址在这里:ArcGIS 9.4:Desktop Usability
flv格式(体积更小)
wmv格式(更清晰)

        在这里拣重点的东西翻译一下:
        题外话1:Jack说,过去的三年时间里,我们都致力于9.4版本的开发,有时候我们也称它为version 10,但最后我们还是将版本定为了9.4。(看来ArcGIS 10是呼之欲出了);
        题外话2:9.4版本将可以和9.3.1版本同时安装在一台机器上。
        下面进入特性倒数,由Jack非常赏识的一位名叫John Coken的年轻人演讲:
9、新的用户界面(主要是dockable windows):
可停靠的toc。与visual stuidio的诸多可停靠面板一样,使用最多的table of contents面板将可以停靠。隐藏toc后,屏幕可以显示更多的地图内容:


并且,当toc从自动隐藏或从自动隐藏中恢复时,地图内容不需要刷新。
集成到的arcmap中的catalog管理界面(可以将其他的地图数据从此界面中直接拖入,以实现add data的功能,此功能将会greatly improve your productivity):

并且也可以进行停靠:


8、新的Attribute Table界面:
图层的属性表也实现了停靠,默认是在窗口正下方:

可同时打开多个图层属性表。虽然现在版本中也可以实现,但实际中打开两个或以上属性表后,程序的可操作性大大下降,不得不在多个属性表中来回切换。现在可以将他们作为多个标签页,同时放在可停靠窗口中。除此之外,还可以在可停靠窗口中并排显示多个属性表内容,便于进行内容比对:


7、强大而实用的即时搜索功能:
使用这个搜索面板,可以轻易搜索出多方面的内容:

比如,需要额外的地图数据,像google那样在这里进行搜索,从列出的结果列表中,将图层拖拽到地图上即可实现添加地图数据。可以看出,搜索是在ArcGIS Online上进行的,随着以后ArcGIS Online的不断强大,会有越来越多的用户将自己可以共享的数据发布到AGOL上面,使得AGOL逐渐成为一个强大的“云端”,相比于自己购买的成本或为者获取数据的难度,既方便又快捷,何乐而不为呢?虽然AGOL从理论上的强大到实际中的可用,还有很长的路要走,但不可否认这是一个新颖而正确的趋势,所以不熟悉新版AGOL的朋友,赶紧熟悉一下吧。(推荐flyingis大侠的一片博文,另外参见9.3.1中的LayerPackage
除此之外,还可以对GP工具(arctoolbox里的内容)进行搜索(搜索本地的内容,带有auto completion的功能),快速定位到需要使用的工具,在结果列表中点击即可打开:


6、新的报表功能:
通过向导式的创建方式,可以轻易的从现有数据创建出复杂的报表(样子比较像DevExpress控件开发出来的报表),包括可选的样式模板。



从最后一幅图可看出,创建完成后还可以对报表的模板进行编辑,随时插入或删除需要补充和多余的内容。

5、更强大的ModelBuilder和Geoprocessing:
Geoprocesssing,是GIS的核心。在ArcGIS的桌面产品中提供了源远流长的ModelBuilder和Python Scritps。 ModelBuilder可以快速,直观,方便的为自己的问题定制科学的解决办法,与编程处理方式是相得益彰。94版本中的ModelBuilder,当把鼠标移动到一个tool或parameter上时,可以不通过右键,直接以tooltip的方式查看到它的所有属性:

还有一个比较令人兴奋的地方就是ModelBuilder中终于加入了Undo和Redo的功能(上图中红圈的部分包括对Auto Layout和误删除的恢复),经常因为手快而误操作的朋友可以宽心了。
此外,还允许在customize的过程中,将自己常用的tool或者model直接拖拽的任何一个toolbar上,以实现更快捷的访问。
再此外,对于任何一个tool,在执行的时候,新的Geoprocess options里面有一个background Geoprocess选项,可以使得此工具的每次执行都在后台进行,在执行的过程中,除了有右下角的进度条,用户仍可在前台对ArcMap进行任何操作,结果出来后会以气泡的方式进行提示。对于耗时的工具来说,这是个令人兴奋的功能。

4、TOC中新的Layers标签页:
演讲者将其称为smart legend。现在的TOC中有Display,Source和Selection三个标签页,最后这个更是少用。这个新的Layers标签页,会根据当前范围内的,可见的地图内容来显示图例,不在当前范围,或者不可见的内容,都不会进行显示;而移动和地图范围或者改变了图层的可见性(可在Layers标签页中直接更改图层可见性和可选择设定)后,面板中的图例会动态发生变化,仍然只显示当前范围内的可见图层的图例。
点击某个图例后,使用该图例的所有feature会以动画的方式在地图上进行闪烁,非常直观的定位出所有使用该图例的feature。


3、可以搜索的Symbol Selector:
目前版本的ArcMap,若想改变一个图层的符号,单击该符号后,在Symbol Selector中只能凭肉眼去从近2000个符号(默认情况下)里寻找想要的符号,94中,如下图,有了搜索工具条,可以直接输入想要的符号名称,即可快速定位到该符号。比如直升机符号输入helicopter即可:


2、ArcMap变得更加Time-Aware:
每个图层属性对话框中将有一个新的Time标签页,类似92版本推出的Animation功能,可以更容易和快速的展示时间有关的地图数据,比如10年的火场历史等。

提示:视频中这个特性讲完后,小伙子会展示特有的美式肢体语言,来博得现场的掌声,也是我们演讲过程中所应该学习的内容:)

1、Fast Basemaps:
首先看下图:

现阶段非常别扭的一个用户体验就是,当移动地图后,仍然按住鼠标左键,会看到地图新范围上并没有内容,而是大片的空白,释放鼠标左键后,此区域的内容才会重绘。
94版本中,可在data frame中通过右键新建一个basemap layer,实际上是一个特殊类型的group layer,之后,可以再toc中,把想要作为底图的所有图层添加到这个basemap layer下。之后,移动地图,神奇的事情发生了:不论怎样快速移动,basemap layer下的几个图层都会始终呈现(实际上是快速重绘),再也看不到任何的空白了……bravo!
(现阶段:ArcGIS Engine中,使用Dynamic Display技术的地图也会有类似的体验;ArcGIS Server中,如果是做过缓存的map service,而且缓存获取的速度足够快(比如在本机上,不受带宽的影响),也会有类似的体验。)

2009年9月13日星期日

从arcgisscripting到arcpy——Geoprocessing在前进

        仔细查看9.3.1中本地的Desktop帮助会发现,没有任何先兆的情况下,An overview of writing geoprocessing scripts主题介绍了Arcpy模块,与在线帮助中该主题的内容完全不一样。而这个Arcpy就是9.4中arcgisscripting的升级版。文档编写人员在本地帮助中为9.4打了一个广告:)
        从win32com到arcgisscripting,geoprocessor实现了跨平台;从gp.create()到gp.create(9.3),脚本中许多对象得到了质的升级。而arcpy正是arcgisscripting的继承者,它将为我们带来包括数据分析,数据转换,数据管理,及工作自动化在内的更加智能化的编程体验。在ESRI 2009 UC的视频中也能看到,ArcGIS Desktop 9.4中将Python环境更紧密的结合到了ArcMap中来。这也再次说明了Python作为scientific programming language for GIS的重要性。
        Geoprocessing是ArcGIS产品的三大核心(Geodatabase,Geovisualization,Geoprocessing)理念之一,也是AGS中gp服务的基石。Desktop之所以强大,在很大程度上取决于Geoprocessing Framework的支撑。若要深入掌握Geoprocessing,除了反复操练ArcToolbox,还得写的一手好的Python Scripts才行。

2009年9月7日星期一

Local方式连接成功,Internet方式连接失败的原因之一

        以前也看到论坛中有朋友说这个问题,前两天刚好碰到,记录一下,方便其他朋友查阅。
        症状:
1、在ArcCatalog中,通过Local方式连接gis server没有问题,通过Internet方式连接(首先保证输入的字符串格式正确)提示:the request method (get.post,ect) was not allowed。。。。
2、ADF开发时(比如新建一个web mappling application模板),在MapResourceManager中,通过Local方式连接数据源没有问题,通过Internet连接时出错如下图:

        解决办法:很有可能是安装时未进行Web Application Post Install引起的,(重)做一遍Web Application Post Install即可。

2009年9月6日星期日

《成为简·奥斯汀》+《苏菲的世界》

        昨晚做了个梦,感觉很深奥,赶紧记下来。
        眼前是草坪,远处是树林。有个女的拿了几封信,放到草坪上的一个邮箱(《苏菲的世界》)里,开心地跑开了;后面跟来一个男的,面对那个女孩跑开的方向,背对邮箱,用手从后面取出那几封信,露出一种描述不清的表情,确切的说应该是看不太清。这时候一个旁白的声音响起了:
        找到这几封信对研究简奥斯丁(《成为简·奥斯丁》)的生平非常有帮助
        ……
        男孩根本不愁没有漂亮的女孩来找她
        但眼前这个女孩充其量不过是有一双大眼睛罢了
        尤其是面对生死离别的时候,她的眼神简直是脆弱不堪
        ……

2009年9月4日星期五

不得不说的《银河时代》!

        先说结论:人生的道路上会碰到各种各样的东西和事情,先来的总比后到的给自己留下的印象要深刻;如果又是在一定的时期,比如珍贵的童年,碰到了吸引自己的东西,那么ta们留给你的印象和感觉则是,不可磨灭。
        现在看来,我没有沉溺于电子游戏之中,不是因为自己不喜欢玩游戏,而是,幸好是,早在之前把玩电子游戏的激情用光了。最早电子游戏当然就是小霸王游戏机(日本人起名叫做fc, family computer),那时候幼儿园都还没有毕业,每次按电源键都有李阳叔叔的一句:啊哈,小霸王其乐无穷!还记得第一次从舅舅家拿来小霸王的时候,全院子(那时候是平房)的小朋友还有邻居的大人都围过来看(这种场面以后再也不会有了:(),而第一盘卡带我还记得清楚,是白色的二合一:魂斗罗一代和台球……后面就有三合一四合一的卡带,还有一盘比较经典的是42合一,四个选择画面,超级玛丽是第四个画面中的第一个游戏……那时候真是其乐无穷!上了小学以后就天天盼着星期天,因为星期天的时候爸爸同意可以玩半天的游戏;到后来我就天天盼寒暑假,因为寒暑假的时候可以天天玩小霸王!当然也是趁家长上班去的时候,所以一般假期的情况就是,早上装睡,等父母上班刚一出门,就爬起来开始打游戏,等到差不多他们快下班回来的时候,提前半个小时关机(得让机器凉下来),开始做假期作业,早上下午都是如此。结果就是假期作业人家早早做完,而我的总是拖到最后几天……上初中以后小霸王就退休了,换了新的裕兴学习机,而我觉得这个机器所有的学习功能都很失败,唯一成功的反倒是它的游戏功能,因为它把很多的fc游戏放到了3.5寸的软盘里,一般是二合一,去陵园路北段那有个裕兴代理商,里面有报纸,上面印着所有的游戏目录,一个软盘的游戏玩完了可以再去更换,两块钱换一张盘,相比之前的一盘卡动不动就100块以上,那是划算极了。于是每个星期天我就骑车子去那换盘,不过都要搭一盘学习软盘回来掩人耳目……
        直到有一次,让我换到了《银河时代》这个中文RPG游戏(现在才知道它其实就是鼎鼎大名的Final Fantasy系列第一代),现在回想起来当时的情景可谓是玩得天昏地暗。一个人玩不过瘾,把院子里的mc也叫过来一起参谋,尤其在寒暑假,日以继夜,夜以继日……后来就把报纸上带RPG字样的游戏都挨个换回来玩,又陆续玩了《宇宙战将》(FF2),《甲A足球》,《英烈群侠传》,《镜花缘》,《天使之翼》等,每个游戏都够玩上一个假期。再后来才知道RPG是role-playing game的缩写……现在我也才知道,在国外,想玩到这个游戏至少有人花了75美元,因为有个帖子的标贴是“The best $75 I ever spent”
        以前的c600上也有fc的模拟器,也玩了以前没有玩过的《封神榜》,但是熟悉的几个游戏的rom读取都有问题,十分可惜。这两天终于找到了在ppc上可用的《银河时代》《宇宙战将》的rom,结果就是昨天手机的电一天就用完了。
        好时光就到这吧,最后贴上几个这几天反复研究的帖子,碰巧和我有同样经历的朋友们可以收藏起来,以备后用:)




        《最终幻想1》FF1图文攻略
        《最终幻想1》详尽攻略
        最终幻想1全资料
        国外的一个网站gamefaqs里的FF1

2009年9月1日星期二

please newnaw now!

        通过SeaDragon,终于把newnaw的顶端给补上了。以前一直留那么一块,不知道放什么合适,今天匆匆在newnaw的存档中选了100张照片,ps了一下,觉得现在这个就比较合适。看一下所谓的newnaw top100 吧:)

2009年8月30日星期日

Deep Zoom(Silverlight) VS GigaPan(Flex)

        自己用相机照了照片,想与全世界的人分享,没问题,有FlickrPanoramio甚至google的street view。但通常受限于网络原因,都是把照片resize之后再上传,一些精彩的全景或者大幅的照片就没办法与他人分享了。在相机厂商疯狂追求千万像素的时代,一些大分辨率照片当然也忍不住向大伙展示了。在这方面就要RIA来做了,两大平台现在都有类似的应用,大家可以比较一下:
Flex的GigaPan.org
Silverlight的SeaDragon
        ps1:若要上传自己的照片,gigapan需要注册账户,然后下载一个客户端工具,安装后30m左右,且要求上传照片像素(长*高)在5000万以上;seadragon只需要一个图片的url即可,生成的程序可以嵌入到支持html的页面中;
        ps2:seadragon使用的是Silverlight的Deep Zoom技术;
        ps3:上传一张照片(07年许巍演唱会散场),大家看看seadragon的效果:)

2009年8月26日星期三

从KIS到ESS

        过去两年一直用卡巴斯基KIS8,因为有很长一段时间:1、迅雷开启,cpu占用率100%;2、迅雷下bt,占用率100%;3、qq传文件,占用率100%;4、支付宝插件,提示检测到key logger,虽然最后一条不是卡巴的错,但前三条已经忍不了了,而且找不到解决问题的有效办法。
        今天把机子里的门神换成了ESS4,就是那个ESET的NOD32,到目前为止,4个问题全部解决,总算不用把卡巴的防火墙开来关去的了,sigh……

2009年8月21日星期五

相机啊相机

        随着佳能09秋季新品发布会的露面,看来自己的机器又成了昨日黄花:(
        昔日的佳能G10和LX3的对决,GX200勉强凑个热闹。G11看上去比G10改进不太多,主要是画质的提升,是软指标;但随着S90的出现,消失了近4年的SX0系列又复活了,而且看评论,S90的对手直指LX3,看起来她的外观就比LX3低调的多,加上“在ISO 1600下,得到了难以想象的美丽且低噪点图像”,简直是羡慕啊!呵呵,继续yy吧~~btw,理光匆匆推出CX2,仅仅6个月后就否定了CX1,不仅揠苗助长,也是对消费者的不负责啊。
        卡片的体积,单反的性能,等若干年以后出现了这种心目中理想机型的时候,再考虑换机器吧:)
        btw,这两天又是两年一度的田径世锦赛了,依然是杨建解说,依然是那么不卑不亢,依然是专业与激情兼有,依然是独当一面,非常喜欢他的风格~

2009年8月18日星期二

台北市也有了street view

        google map的街景大家都知道,是大众gis应用中的一大利器,探寻旅游线路的极佳工具。今天开始我国台湾省台北市地区也有街景视图了。效果相当的不错:

        放大些看看:

        站在著名的101楼下:

        抬头看看台北的地标:

        还可以随时点击右上角的“用户图片”,查看该地点附近,由用户上传的照片,很贴心的一个功能:

        不知道将来的某一天,google街景车准备开入北京的时候,相关部门会不会在全球其他所有国家都欢迎街景车的背景下,以某种理由进行阻拦呢?