Monthly Archives: May 2009

恍如隔年

又是一年答辩时。即使去年答辩那天没有地震,其他情景也都历历在目,偷吃着给专家评委准备的水果,忙着打拨不通的电话,和老魏小崔走过育才路的转角,zw宣布他荣登大师兄宝座,晚上唱歌拿了把伞……一年的时间一晃就过去了,想想发生过的事情,仿佛都在昨天一样。
其实很佩服和羡慕小李他爸。为了自己精神上的追求,只身一人云游四方,与陌生人交谈,与自己的思想沟通,路途中会有无数经历,主要是思想境界的升华。也不用和这个主任那个领导打交道,虽然没有路虎开,但远胜过那些呼风唤雨,拍板拿事的人。有时候也在想,面朝大海春暖花开。但没有经历过,即使在背靠大海,有小桥流水绿树青草的山间,过着天天耕地喝酒的日子也会觉得烦。努力工作,换来好的生活,是一件再也正常不过的事情。但最后的追求到底是什么?金钱?荣誉?幸福?想起一句歌词:阿拉善突然来了沙尘暴,把所有白领吹成黑领了……前一阵子李文华老先生去世,感动之余留给我印象最深的是为人低调,淡泊名利这八个字。
If you really want it, push the past pain, use it, control it, feed everything to your fire, pains, failures, critics, glories…if you really want it.我的fire大概已经在错误的追求中熄灭了吧。
5:30,下雨的清晨,湿漉的街道,没有行人。扫扫记忆,像歌中唱的那样:出发啦,不要问那路在哪,迎风向前,是唯一的方法……

China mobile

        昨晚打10086,里面说现在是23点以后,您确定要使用紧急服务吗?紧急服务只能停开机云云,难道他们赚的比以前少了,把值夜班的人都撤了?害的今天占用23点以前的宝贵时间继续打10086,就问个关于机器呼叫转移设置的问题,说了半天都不清楚,给人感觉打10086只能单向办理收费业务,就是只能办理,不能退办。果然,我说要取消短信业务,结果被告知只能拿身份证去营业厅办……办的时候怎么那么痛快啊,电话,网上,短信一不小心就办成功了,退的时候就只能去营业厅……我半年才给你们打一次电话,这点要求都不能满足,哎!
        移动去年营业额800亿人民币。前阵子3G的广告是挺好,但是和人一样啊,不能光穿衣服漂亮,主要还得心灵美。不得不让我觉得它跟学校网络中心一样恶心,就是只收钱不办该办的事。

Bright Star

        金棕榈奖唯一的女得主Jane Campion(获奖作品是反映女性细腻心理的《钢琴课》,是部好电影),携19世纪诗人John Keats的爱情诗《Bright Star》的同名电影又去戛纳了……还没看过电影,到是对这首诗比较感兴趣。
        写作背景就是电影情节,关键是作者只有25岁不到的年龄(就去世了)。找到了一个比较完整的注释版本,也有明喻暗喻之类的分析,估计是外国语文课上的内容。从深蓝夜空中的星星,到宗教隐士洗礼的流水,还有柔软飘落的新雾,山顶上的白雪,爱人的tender breath,仿佛一次优美的时空旅行……但最后还是没能避开永远的话题:永恒和死亡。

Bright star,would I were stedfast as thou art—
Not in love splendor hung aloft the night,
And watching,with eternal lids apart,
Like nature’s patient,sleepless eremite,

The moving waters at their priestlike task
Of pure ablution round earth’s human shores,
Or gazing on the new soft-fallen mask
Of snow upon the mountains and the moors;

No—yet still stedfast,still unchangeable,
Pillow’d upon my fair love’s ripening breast,
To feel for ever its soft swell and fall,
Awake for ever in a sweet unrest,

Still,still to hear her tender-taken breath,
And so live ever-or else swoon to death.

注释:
stedfast:永恒的,不变的
aloft:高高在上的,夜空中的
lids:眼皮
Eremite:有宗教背景的隐士
pure ablution:洗礼

        想起一首同样想象空间极大的情诗,作者是木子。《我纷纷的情欲》:

尤其静夜
我的情欲大
纷纷飘下
缀满树枝窗棂
唇涡,胸埠,股壑
平原远山,路和路
都覆盖我的情欲
因为第二天
又纷纷飘下
更静,更大
我的情欲

ArcGIS API for Silverlight开发入门(8):在程序中使用Virtual Earth的服务

        Silverlight API中还包括了一个ESRI.ArcGIS.VirtualEarth.dll类库,让我们可以方便的访问到老东家的VirtualEarth服务。目前Silverlight API中提供的VirtualEarth服务有三种:Map,Geocode和Routing,不过一看就知道后两种服务对于国内的数据来说又无缘了。
        直接看如何使用它的Map服务获取地图数据吧。同前,新建一个Silverlight工程,添加ESRI.ArcGIS.dll和ESRI.ArcGIS.VirtualEarth.dll的引用,引入xml命名空间,在xaml里面这样写:





        可以看出,和添加其他图层基本是一样的。SIlverlight API中针对VE地图的图层类型是TileLayer,LayerStyle有三种:Road,Aerial和AerialWithLabels,分别对应矢量图,影像图和带街道标注的影像图。ServerType就比较特殊了,有两种:Staging和Production,分别对应访问VE服务的账户类别,前者是免费的,后者是收费的。如果你此时运行程序的话,那是看不到地图的,因为TileLayer还有个关键的token属性没有设置。
        VE的服务那是相当安全,每次访问VE的服务,都要提供一个token(一个加密字符串)来进行身份验证,而这个token又是根据TokenService自动生成的,要通过TokenService生成一个token,又需要一个合法的Microsoft Virtual Earth Platform developer account……明白了这个过程,就来做我们的工作吧。
        首先,去申请一个Microsoft Virtual Earth Platform developer account,当然之前你还得有一个Windows Live账号。申请的这个账号是Evaluation版的,所以决定了以后我们只能使用Staging的服务,如果要把它变成Production版本,可以通过邮件联系微软,然后缴费;
        之后到注册时所填的邮箱去激活申请的Microsoft Virtual Earth Platform developer account账号,然后为其设置密码(必须是8-14为之间,包括大、小写字母,数字,且还要有非字母数字的字符,和windows server 2008是一样的),我们平常肯定不会这样设置密码,为了以防万一,建议赶紧把设置好的密码记录下来,
没准哪天就忘了。现在就可以用这个账户和密码来访问TokenService,通过它生成token,交给TileLayer的token属性。
        为了安全目的考虑,token是不建议也不能直接在Silverlight程序中进行设置的。那么怎么办呢?这样办:1、通过装载Silverlight的aspx页面的Page_Load方法,来申请我们的token,并把它添加到Silverlight的初始参数中,2、然后当Silverlight插件载入的时候,把token读出来,3、在Map_Loaded事件中,赋给TileLayer。
1、通过TokenService申请token:
在web app中add web reference,url用https://staging.common.virtualearth.net/find-30/common.asmx?wsdl,起个名字叫VirtualEarthService.TokenService。

其中Xaml1是Silverlight插件的ID:
2、Silverlight插件载入时读出这个token。在App.xaml.cs中:
private void Application_Startup(object sender, StartupEventArgs e)
{
VEtoken = e.InitParams["token"];
this.RootVisual = new Page();
}

3、最后在加载地图控件后,交付token:
private void Map1_Loaded(object sender, RoutedEventArgs e)
{
foreach (Layer layer in Map1.Layers)
if (layer is TileLayer)
(layer as TileLayer).Token = (Application.Current as App).VEtoken;
}

        终于能看见VE的图了。当然,我们的开发账户是免费的,所以地图上有很多“Staging”麻点(每个tile一个):

        至此,ArcGIS API for Silverlight的开发入门已经讲完了,我和大家一样也是边学边写的,刚好这两天SIlverlight API又升级了第二个Beta版。其实Silverlight和Flex一样,能使传统的WebGIS散发出全新的魅力,从而使我们的程序在RIA的道路上大踏步前进,能够做出什么样的效果也基本只受想象力的制约了。随着Silverlight 3的推出,我们也有理由相信Silverlight的明天会更好。

Last Chance Harvey

        《哈维最后的机会》,看着看着我就觉得像是老年温馨版的《日出之前》,最后的结局又比《幸福终点站》来的要舒服。

        Harvey Shine,笑容就和他的last name一样,辛勤工作的他为了去伦敦参加女儿的婚礼,丢了工作,到了伦敦后先被前妻数落一番,后被女儿告知决定让继父代替他参加婚礼……事实证明,不是老哈维人不好,只是别人比他更适合那个家庭。Kate Walker伤心哭诉的时候,老哈维也没有去给一个可靠男人的拥抱,还是让她自己做出了决定。
        “我能把这当做是还有希望吗?
        如果是的话,给我个明确的微笑。”
        musicman说,青春还没有掏出手枪我们就倒下了,永远的倒下。但这不也是在生活之路上的重生吗?没有了青春,但我们的生命还在继续。
        和《日出》《日落》一样,这种片子好像只适合在欧洲拍摄,不管是欧洲小镇还是大城,阳光总是显得特别充足,即使是在一天中最冷的日出前后,也感觉不到一点寒意。整个城市都非常干净,路边的落叶也只会让画面显得更加清新,看了就让人直想去哪里呆一段时间。
        另插一句,在L world里头也看见人家老外掏钱上作文课。我觉得一个小孩,要是不要让他在中小学写12年的作文,把娃一直憋着,等他长大了,抛开他掏钱去上writing class不说,肯定会写一篇丰富感情自然流露的好文章出来,为啥?憋的慌么,就跟那啥道理是一样的,至少也会保有对写作这一美好事物的兴趣。
        其实看到这个片子的介绍是去年冬天一个早晨,最近发现它的时候才想起来还有这么一茬事,又让我想起了当时的情景,挺有意思。

ArcGIS API for Silverlight updated

        Silverlight API升级了,但还在继续它的beta之旅(Build 209,之前是Build 160),看来要修正的地方真不少。
        除了新出一个OverviewMap的widget,改进了Magnifier widget(减轻使用时的服务器负担),还允许在Map控件初始化的时候根据Extent来动态投影地图,这与Flex API是一样了……具体的变化请看这里

ArcGIS API for Silverlight开发入门(7):使用非AGS数据源的图层

        通过上一节学习,可以看出在Silverlight API中不仅可以轻松使用ArcGIS Server 9.3发布的地图服务,也可以通过继承相应的图层,引入其他的数据源,比如ArcGIS Server 9.2发布的地图服务,WMS服务,或者其他免费的数据。本节就通过一个实例,来看看如何将Google Map作为底图数据。
        Google Map是经过缓存的数据,所以需要继承的是TiledMapServiceLayer。那么在扩展这个图层的时候需要做哪些工作呢?首先就要明白地图缓存的原理。可以看出我们继承的这个图层,需要收集到以下几个信息:
1、Tiling Scheme Origin;
2、切图的范围,也就是FullExtent;
3、SpatialReference;
4、TileInfo,包括切图的大小,级数,以及每级的Resolution;
5、最后就是重写GetTileUrl方法。
        这是为什么呢?可以想象,当地图控件的范围改变时,能够获取到当前范围的信息,那么只要把左上角和右下角之间的Tile全部按顺序显示出来就行了。由前面的文章可以看出,当图层获取了1、2、3、4四个信息后,图层完全可以自动计算出所需的Tile,最后根据GetTileUrl方法取回这些Tile显示出来即可。
        那么对于Google Map的前4个参数,如何取得呢?记得在Catalog中做缓存时,有一个Load Tiling Scheme from Google Map吗?按照这个Tiling Scheme将一个地图服务做缓存,然后查看它的conf.xml和Service Directory,便完全可以取得这几个参数了。另外关于如何获取Google Map的缓存,网上已经有非常多方法,这里就不再讨论了。


        代码如下:
public class GoogleMap:TiledMapServiceLayer
{
public override void Initialize()
{
this.FullExtent = new ESRI.ArcGIS.Geometry.Envelope(-20037508.342787,-20037508.342787,20037508.342787,20037508.342787);//(-180, -85.0511287798066,180, 85.0511287798066)
{
SpatialReference = new ESRI.ArcGIS.Geometry.SpatialReference(102113);
};
this.SpatialReference = new ESRI.ArcGIS.Geometry.SpatialReference(102113);
//this.InitialExtent = this.FullExtent;
this.TileInfo = new TileInfo()
{
Height = 256,
Width = 256,
Origin = new ESRI.ArcGIS.Geometry.MapPoint(-20037508.342787, 20037508.342787)//Origin = new ESRI.ArcGIS.Geometry.MapPoint(-180, 90)
{
SpatialReference = new ESRI.ArcGIS.Geometry.SpatialReference(102113)
},
Lods = new Lod[20]
};

double resolution = 156543.033928;
for (int i = 0; i < TileInfo.Lods.Length; i++)
{
TileInfo.Lods[i] = new Lod() { Resolution = resolution };
resolution /= 2;
}

base.Initialize();
}

public override string GetTileUrl(int level, int row, int col)
{
//google maps map
//string baseUrl = "http://mt0.google.com/mt/v=ap.92&hl;=zh-CN&x;=";
//string url = baseUrl + col.ToString() + "&y;=" + row.ToString() + "&z;=" + level.ToString() + "&s;=";
//return url;

////google maps satallite
string baseUrl = "http://khm2.google.com/kh/v=38&hl;=zh-CN&x;=";
string url = baseUrl + col.ToString() + "&y;=" + row.ToString() + "&z;=" + level.ToString() + "&s;=";
return url;
}

}
        需要注意一点,Google Map采用的是WGS 1984 Web Mercator投影,这个投影的wkid在REST API中查不到,但在Service Direcotry中可以找到,是102113。另外,重写DynamicMapServiceLayer也是基本相同的。
        之后也可以按照这个Tiling Scheme对自己的服务作缓存,自己的数据和Google Map便可以叠加在一起了。但是这样子使用Google Map的数据不仅担心会被封IP,而且更重要的是版权问题,毕竟不像JS API(有ArcGIS JavaScript Extension for the Google Maps API )或者Flex API(有Google Map API for Flex)。别忘了MS有自己的Virtual Earth,下一节中就来看看如何在我们的程序中名正言顺的使用VE的数据吧。

ArcGIS API for Silverlight开发入门(6):图层类型小结

        在用Silverlight API开发的过程中,不论是从客户端提交到服务器端的数据,还是从服务器端返回客户端的数据,都要表现在浏览器中,具体的来说是Map控件里。但根据各自类型的不同,比如数据源,地图服务的类型,是否缓存等,决定了它们将处于某个图层里,前面讲过的GraphicsLayer就是一种图层。清楚地认识这些图层类型,对于处理于服务器与客户端之间的地图数据来说是很重要的。
        所有的图层都是从Layer类型继承而来的,可以参考下载的API中的对象模型图。
Layer
  |–TiledMapServiceLayer
  |       |–ArcGISTiledMapServiceLayer
  |–DynamicLayer
  |       |–DynamicMapServiceLayer
  |                 |–ArcGISDynamicMapServiceLayer
  |                 |–ArcGISImageServiceLayer
  |                 |–GPResultImageLayer
  |–GraphicsLayer
  |       |–FeatureLayer
  |–ElementLayer
        下面就按顺序认识一下这些图层吧,也包括Silverlight API中独有的FeatureLayer。

1、Layer:
        继承自Silverlight中的DependencyObject,并实现了INotifyPropertyChanged接口,是Silverlight API中其他图层的基类。可以把它看成麦子,再好吃的凉皮,泡馍都是由它做出来的;

2、TiledMapServiceLayer:
        继承自Layer,是所有使用了缓存的地图服务的基类。通过它可以在程序中加入经过缓存的,来自不同数据源的地图服务。比如ArcGIS Server的地图服务,Google Map的地图,Virtual Earth的地图等;

3、ArcGISTiledMapServiceLayer:
        继承自TiledMapServiceLayer。像上面说的一样,这个图层扩展了TiledMapServiceLayer,于是支持由ArcGIS Server 9.3版本发布的经过缓存的地图服务;又比如ArcGIS Server 9.2版本发布的缓存地图服务不支持REST方式连接,如果要在93的客户端API中使用的话,就可以通过TiledMapServiceLayer扩展一个比如ArcGISTiledMapServiceLayer92,来支持92Server发布的缓存地图服务;

4、DynamicLayer:
        继承自Layer,是动态地图服务的基类;

5、DynamicMapServiceLayer:
        继承自DynamicLayer,对应于TiledMapServiceLayer,要使用未经过缓存的动态地图服务,就得通过扩展这个图层来实现;

6、ArcGISDynamicMapServiceLayer:
        继承自DynamicMapServiceLayer,针对ArcGIS Server 9.3版本发布的动态地图服务。同理,如果要在客户端API中使用其他动态地图服务,比如OGC的WMS服务,则也需要像这个图层一样,扩展上面的DynamicMapServiceLayer来实现;

7、ArcGISImageServiceLayer:
        继承自DynamicMapServiceLayer,针对ArcGIS Server 9.3版本发布的Image Service,因为影像服务也属于动态的地图服务。在客户端API中,可以通过ArcGISImageServiceLayer的一些属性,方便通过浏览器来展示服务器端的影像数据,比如通过BandIds属性,可以快速调整影像数据显示波段的组合(RGB通道),提供不同结果供用户查看。点击这里,查看一个实例;

8、GPResultImageLayer:
        继承自DynamicMapServiceLayer,针对Geoprocessing服务所产生的结果。可以请求服务器端的GP服务将结果动态生成一张图片,将此图片作为GPResultImageLayer图层直接添加到Map控件中;

9、GraphicsLayer:
        继承自Layer,是图形数据集中展现的地方,在第四讲中已经详细讨论过了;

10、FeatureLayer:
        继承自GraphicsLayer,这也是Silverlight API中的亮点之一,通过它可以完成一个比较炫的功能:

        整个过程在xaml中就可以实现,只需要在Map的Layers中插入以下代码即可:
<esri:ArcGISTiledMapServiceLayer ID="StreetMapLayer" Url="http://server.arcgisonline.com/ArcGIS/rest/services/ESRI_StreetMap_World_2D/MapServer"/>
<esri:FeatureLayer ID="featurelayer"
Url="http://sampleserver1.arcgisonline.com/ArcGIS/rest/services/Specialty/ESRI_StatesCitiesRivers_USA/MapServer/0"
Where="POP1990 > 75000" ClusterFeatures="True" FlareBackground="#99FF0000" FlareForeground="White" MaximumFlareCount="9"
FeatureSymbol="{StaticResource markersymbol}">
<esri:FeatureLayer.OutFields>
<sys:String>CITY_NAME</sys:String>
<sys:String>POP1990</sys:String>
</esri:FeatureLayer.OutFields>
<esri:FeatureLayer.MapTip>
<Grid Background="LightYellow">
<StackPanel Margin="5">
<TextBlock Text="{Binding Converter={StaticResource MyDictionaryConverter},
ConverterParameter=CITY_NAME, Mode=OneWay}" FontWeight="Bold" />
<StackPanel Orientation="Horizontal">
<TextBlock Text="Population (1990): " />
<TextBlock Text="{Binding Converter={StaticResource MyDictionaryConverter},
ConverterParameter=POP1990, Mode=OneWay}" />
</StackPanel>
</StackPanel>
<Border BorderBrush="Black" BorderThickness="1" />
</Grid>
</esri:FeatureLayer.MapTip>
</esri:FeatureLayer>

        可以看出这个FeatureLayer其实是将一个Query查询封装到了一个GraphicsLayer中。通过url指定查询的图层,where指定查询条件(也可以输入geometry指定查询的图形),最关键的是ClusterFeatures=”True”,当一个范围内feature过多时,就将他们“聚合”在一起,以一个更大的符号表示出来,进一步放大时才将它们单独显示出来,如果聚合的目标不超过MaximumFlareCount设置的数目,那么就会出现那个flare动画。在MapTip(继承自GraphicsLayer)里面进行了简单的设置,一个背景为黄色的Grid里显示两行文字,用一个DictionaryConverter类将返回的Graphic.Attributes集合中的两个字段转换成String类型显示出来。顺便提一下,FeatureLayer也可以用于线或面层的查询,但如果继续使用ClusterFeatures的话就没什么意义了。虽然FeatureLayer封装的比较死,只能有此一种效果,但它提供给我们一种思路,可以结合SilverlightRIA的特性,充分发挥自己的想象力做出更炫的效果来;但是,对于需要展现海量(成百上千个)点数据的图层来说,ClusterFeatures是一个非常有用的特性,毕竟将这么多点同时呈现出来性能还是有问题的。如果不使用ClusterFeatures,看起来应该是这样的:

        不用FeatureLayer行吗?
        说到FeatureLayer,还有两个Renderer不得不提一下:UniqueValueRenderer和ClassBreakerRenderer。它们都是依托FeatureLayer的,用于单值专题图的渲染。具体的用法都比较简单,可以查看API中的Concepts。但Samples中的Thematic Rendering例子并没有采用这两种Renderer,而是人为地为每个Graphic设置了不同的Symbol。目前看来虽然这两个Renderer有点鸡肋,但毕竟是现在3种客户端API中提供的唯一现成的Renderer,可以猜想也许下个版本的Silverlight API中会有更加成熟的专题图Renderer直接供我们使用;

11、ElementLayer:
        继承自Layer,它可以用来专门呈现Silverlight中原生的FrameworkElement,比如视频,音频等。虽然在FillSymbol的Fill属性中也能利用Brush类来展现一段视频,但毕竟有些“小气”,在ElementLayer中可以大大方方的放置Silverlight元素。你可能会问,在Map控件之外,Grid等布局元素中不是也能放置Silverlight的东西吗,为什么要放在ElementLayer里呢?其实有个问题经常困扰GIS开发人员,就是想让一些非地理数据元素随着地图范围的变化(放大,缩小,平移)而变化,而无须自己在Extent变化后重新计算客户端坐标,手工改变这些元素的位置。瞧,ElementLayer正解决了这个问题。

        目前Beta版的API中暂时有这么多图层类型,以后也许会继续增加。但万变不离其宗,无非就是从那几个基类中派生出来的。所以,下一节我们就通过一个实例来看看如何扩展基类的MapServiceLayer,来达到使用非ArcGIS Server数据源的目的。