ArcGIS API for Silverlight开发入门

你用上3G手机了吗?你可能会说,我就是喜欢用nokia1100,ABCDEFG跟我 都没关系。但你不能否认3G是一种趋势,最终我们每个人都会被包裹在3G网络中。1100也不是一成不变,没准哪天为了打击犯罪,会在你的1100上强制 装上GPS。GIS工作既然建立在计算机的基础上,当然也得随着IT行业与时俱进。
       看看现在计算机应用的趋势吧。云(计算),这个东西可讲不清楚,因为云嘛,飘忽不定的。不过可以这样来看它,以后计算机网络上就有一坨(或者几坨)万能的 云,有什么需求云都可以满足我们,包括各种资源或者计算工作,就不需要在自己的机器上安装任何软件了(甚至操作系统都可以由天边那朵云来提供给你)。更具 体点,SaaS(Software as a Service),各种的网页邮件系统,google docs(一 个在线的office)都是SaaS。收发邮件登陆一个网页就行,而不需要在自己机器上安装一个软件。这就是计算机应用的一个趋势,把所有东西都做到网 上。再来看看网上的趋势:RIA(Rich InternetApplication)。RIA简单来讲就是一个网页(网络应用),在完成基本功能的同时,会让你觉得很漂亮,操作起来很舒服,效果很 炫,而不是打开后立刻就想关掉它。其实大受欢迎的开心网(各种插件)和twitter,甚至QQ空间等,都有RIA的身影。
       好了,ArcGIS之所以在行业领先,特点之一就是它能紧跟计算机发展的趋势。ArcGIS Online就是那朵天边的浮云;JavaScript API,Flex API,Siverlight API就是ArcGIS自己RIA的三驾马车。
       这里还得插一句,我觉得ArcGIS Server的主角本来是ADF,通过它我们可以完成一个无所不能的ServerGIS。但在大多数情况下,GIS都是作为特定的业务嵌入在一些MIS中 的,相比购买ADF这辆悍马来说,还是直接驾驭三套车跑的更轻快一些。
       现在看看我们的主角。其实是ArcGIS API forSilverlight/WPF(以下专注Siverlight部分),那么Silverlight和WPF的关系如何呢?Silverlight 原来叫WPF/E,E就是Everywhere,从命名可以看出它们的关系:Silverlight是WPF的一个子集。WPF 是.NETFramework 3.0的组成部分之一,微软视其为下一代用户界面,总之很高档就是了(在Vista和Windows7中看到的大量与XP不同的界面,就是WPF的身 影);Silverlight可以看做是WPF在浏览器里的一个外挂,用于向网络用户展示自己的强大能力,由于受限于网络环境,所以是WPF的一部分核心 功能。Siverlight的设计初衷是跨平台,跨浏览器的。
       如果这些还是比较抽象,那么可以造一个排比句来进一步说明。之前先肯定一点,Flash现在在网络中的的主导地位。开始造句。Adobe有Flash,微 软有Silverlight;Adobe有AIR,微软有WPF;Flex有mxml,Silverlight有xaml;Adobe有 ActionScript,微软有Code-Behind(C#/VB.NET)或者JavaScript;Adobe有CS(包括 Dreamweaver,Flash,Fireworks,Photoshop,Illustrator),微软有ExpressionStudio(包 括Blend,Web,Design,Media,Encoder)。现在,能够看来Siverlight到底是何方神圣了吧?
       最后再来说说ArcGIS这三驾马车(JavaScript API,Flex API,SiverlightAPI)。国外有人说,随着Siverlight API的推出,与Flex API一起,将会使JavaScriptAPI慢慢退出历史舞台,因为前两者就是为RIA而生的。但其实也不然,随着Google和Mozilla工程师 的推进,他们能够使JavaScript的执行速度提高非常多,Chrome就是例子。在这种背景下,一些非常cool的程序员会让古老的JavaScript获得重生。到底哪匹马跑得更快?别回答这种问题,赶紧挑一匹自己的马儿,快马扬鞭吧~~
       在今年的ESRI开发用户大会上,一阵鼓声过后,ESRI隆重推出了ArcGIS API for Silverlight/WPF(beta)。接下来我将把自己在学习Silverlight API中的一些经历和大家分享,与大伙共同进步。

原作者:diligentpig

原文地址:http://bbs.esrichina-bj.cn/ESRI/thread-43923-1-1.html

ArcGIS API for Silverlight开发入门(1):Getting Started

这一节来对Silverlight API(ArcGIS API for Silverlight,下同)的开发有个总体的认识。
        欲善其事先利其器。要做开发,第一步就得搭建环境。因为是在Siverlight基础上做开发,所以先得整理好Siverlight的开发环境。Silverlight并没有内建在VS2008中,而是作为add-on的形式附加的。在这里可以找到详细的安装步骤:

        说明一下,步骤1安装了Silverlight add-on(要求有IDE的SP1补丁包);步骤2安装的是ExpressionStudio中的ExpressionBlend,这个工具相当于可视 化的xaml编辑器,可以用来轻松的创建Silverlight程序的用户界面;步骤3中安装的是Silverlight一种非常华丽的图片处理效果,可 以参看这里的实例;步骤4包括一些可用的Silverlight控件和例子。接下来再去看看Silverlight API的要求。可以看出对于开发ArcGIS Silverlight程序来说,只有步骤1是必须的,其他都是可选的。之后需要从ESRI网站下载Silverlight API(需要免费注册一个ESRI Global账户),以备后用。
        总结一下最常见的安装步骤:1、安装VS2008;2、安装VS2008 SP1;3、安装Silverlight Tools for Visual Studio 2008 SP1。到此,就可进行Silverlight程序的开发了。关于开发环境的搭建,还可以参考yyilyzbc版主的帖子。(做Silverlight API的开发不需要在自己的机器上安装ArcGIS Server,可直接使用ArcGIS Online上的数据;但如果要添加自己的数据,当然还是需要ArcGIS Server了)
        下面就来一个Hello World吧,对于GIS来说,理所当然就是展示一张漂亮的世界地图了。具体步骤如下:
1、VS2008中,新建project,选择Silverlight Application;
2、 在出现的提示框中选择Add a new ASP.NET Web project to the solution to hostSilverlight;(Silverlight程序与flash一样,相当于网页中的一个插件。第一个选项是将Silverlight嵌入到 一个ASP.NET网站中,第二个选项是将Silverlight嵌入到一个临时的html页面中)
3、添加Silverlight API的引用:与.NET程序开发一样,add reference(注意是在Silverlight工程上而不是ASP.NET工程上),找到从ESRI下载的API,选择添加ESRI.ArcGIS.dll;
4、打开Page.xaml,在UserControl标签中添加一句引用,在Grid标签之间添加一些代码,完成后看起来像这样:

  1. <UserControl x:Class="SilverlightApplication1.Page"
  2. xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  3. xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  4. xmlns:esri="clr-namespace:ESRI.ArcGIS;assembly=ESRI.ArcGIS"
  5. Width="400" Height="300">
  6. <Grid x:Name="LayoutRoot" Background="White">
  7. <esri:Map x:Name="mymap">
  8. <esri:Map.Layers>
  9. <esri:ArcGISTiledMapServiceLayer ID="layerworldmap"
  10. Url="http://server.arcgisonline.com/ArcGIS/rest/services/ESRI_Imagery_World_2D/MapServer" />
  11. </esri:Map.Layers>
  12. </esri:Map>
  13. </Grid>
  14. </UserControl>

复制代码

5、按F5,运行程序,就完成了我们的hello world in
GIS可以在浏览器中看到下面的画面:

        看到效果之后,再来对它进行理解吧。
        先说下Silverlight的程序的基本背景。page.xaml实际上是一个控件,相当于asp.net中的default.aspx,大部分 的工作都在这里面完成(app.xaml相当于global.asax);上面的是xaml(读:[ig`zeml])代码,是微软针对 wpf/silverlight的标记语言,与flex中的mxml类似。Silverlight程序中所有的布局工作都是由xaml来完成 的;Silverlight2中,VS2008可以实时对xaml的效果做预览,但是这个预览效果是只读的,对于预览中的控件也不可选;为了弥补这个缺 陷,可以用前面提到的ExpressionBlend来可视化地设计程序界面,会自动生成对应的xaml代码,使用于复杂的布局和美化工作(可参考 Silverlight中的clock例子);再看page.xaml。usercontrol标签(页面的根元素)证明了page.xaml实际上是一 个控件类;下面的几句相当于引入了xml的特定命名空间,里面包括了我们的ESRI.ArcGIS;width和height指明了 Silverlight控件本身的尺寸,一般我们将这里的width和height属性去掉,已达到全屏的效果(你也可以试试哦);Grid标签是布局控 件,相当于html中的表格,可以进行灵活的页面布局,xaml中常用的布局控件还有Canvas和StackPanel;每一个xaml的 Control都可以有一个x:Name属性,以便在code-behind页面中对其引用。
        之后是我们的主角了。Map标签(继承自xaml的Control)相当于一个Map控件,可以在其中加入图层;这里我们添加了一个 ArcGISTiledMapServiceLayer图层(在后面的文章中会专门讲到SilverlightAPI中的图层类型),对应使用的是 ArcGIS Server发布的经过cache的服务,作为客户端的API,同JavaScript与FlexAPI一样,都是通过REST方式对资源和操作进行引用 的;对这个图层,赋予了一个ID属性,因为SilverlightAPI中的图层是从xaml中的DependencyObject继承而来,所以没有 x:Name的属性,为了方便在code-behind(与asp.net类似的托管代码)的代码中找到这个图层,便使用了ID属性;URL的内容便是 ArcGIS Online发布好的一个世界地图资源。
        到此,应该对这个例子理解的差不多了。如果还想再添加一个图层怎么办呢?没错,就是在Map标签中再添加一个layer,不过要注意的是,第一个加入的图层会显示在最下面,并且决定了整个Map控件的空间参考信息。
        大家自然会想到叠加一个自己的数据图层来看看效果,于是对Map标签内容做了修改(china是本机发布的一个中国地图):

  1. <esri:ArcGISDynamicMapServiceLayer ID="chinamaplayer"
  2. Url="http://localhost/ArcGIS/rest/services/china/MapServer" />

复制代码

运行后却还是只有世界地图一个图层(已经确保拼写、大小写正确),怎么回事呢?来用事件帮助查找错误吧。
        Silverlight能够利用.net的一些核心库内容,包括事件。来对刚才的那个图层添加一个事 件:InitializationFailed,当图层添加失败的时候会出发这个事件。添加这个事件的处理也非常简单:在上面的图层中加入 InitializationFailed属性,会提示你生成新的eventhandler,默认回车,看上去像这样:

  1. <esri:ArcGISDynamicMapServiceLayerID="chinamaplayer"InitializationFailed="ArcGISDynamicMapServiceLayer_InitializationFailed"
  2. Url="http://localhost/ArcGIS/rest/services/china/MapServer" />

复制代码

在事件上面右键单击,Navigate to Event Handler,就会进入前面所说的code-behind页面(本例为C#),添加以下代码:

  1. private void ArcGISDynamicMapServiceLayer_InitializationFailed(object sender, EventArgs e)
  2. {
  3. ESRI.ArcGIS.Layer layer = sender as ESRI.ArcGIS.Layer;
  4. MessageBox.Show(layer.InitializationFailure.Message);
  5. }

复制代码

然后运行程序,会得到初始化图层失败的原因:

        原来,为了安全原因考虑,同flash一样,Silverlight对跨域访问也做了严格的限制。要解决这个问题,可以参考帮助中的说明,将两个 xml文件保存在网站根目录,比如C:\Inetpub\wwwroot中即可(其实保存其中一个就可以了,ArcGISOnline已经将两个xml文 件都放在了网站根目录中,所以我们可以引用上面的服务)。
        看下最后的效果吧。

为了更好的理解xaml和Silverlight,建议首先独立完成Silverlight帮助中的两个workthrough:hello world和clock。
原文地址:http://bbs.esrichina-bj.cn/ESRI/thread-44042-1-1.html

原作者:diligentpig

ArcGIS API for Silverlight开发入门(2):一个基础地图实例

这节在一个地图实例的基础上,来对Silverlight API中的一些基本概念做一个总体了解,顺便熟悉一下Silverlight的开发知识。
        点击这里,直接看效果。


        根据上一节的知识,可以知道这个Silverlight程序里包含了一个Map控件,并且里面至少有一个WorldImagery的图层。那么Page.xaml里的关键代码开起来应该是这样的:

  1. <Grid x:Name="LayoutRoot">
  2. <esri:Map x:Name="Map1">
  3. <esri:Map.Layers>
  4. <esri:ArcGISTiledMapServiceLayer ID="WorldImageLayer" x:Name="WorldImageLayer" Initialized="WorldImageLayer_Initialized"
  5. Url="http://services.arcgisonline.com/ArcGIS/rest/services/ESRI_Imagery_World_2D/MapServer" />
  6. </esri:Map.Layers>
  7. </esri:Map>
  8. </Grid>

复制代码

所 有的布局工作都在一个Grid中进行,给它起个名字叫LayoutRoot。Grid里面放了一个esri:Map元素(Map控件),它继承自 Silverlight的Control,所以拥有Width和Height属性,默认是Auto,自动填充整个Grid。Map.Layers是一个集 合,可以往里面添加layer,这里的layer指的是ArcGIS Server或其他软件发布的地图服务,目前SilverlightAPI中支持的能够直接使用的有 ArcGISDynamicMapServiceLayer,ArcGISTiledMapServiceLayer,ArcGISImageServiceLayer, 分别对应ArcGIS Server发布的动态地图服务,缓存地图服务(两种Map Service)和ImageService,这三种图层是拿来即用的,如果你想加入别的地图服务,比如WMS服务,则需要自己继承相应类型的的 Layer;此外还有GraphicsLayer,ElementLayer,SilverlightAPI特有的FeatureLayer等。这些都会 在之后的小节中讲到。强调一下,与ADF开发里MapResourceManager一样,在Map中加入的内容实际上是地图服务,但当做一个layer 处理。
        下面就对这个例子中的每一部分来做说明(与上图中的序号相对应)。

1、当地图移动时获取地图范围。
        当地图范围改变后,显示出当前地图范围的边界值。
        这部分的页面布局是这样的:

  1. <Grid x:Name="Gridright" Margin="0,15,20,0" HorizontalAlignment="Right" VerticalAlignment="Stretch">
  2. <!--extent-->
  3. <Canvas Width="215" Height="110" VerticalAlignment="Top">
  4. <Rectangle Style="{StaticResource rectBottom}" />
  5. <Rectangle Style="{StaticResource rectMiddle}" />
  6. <Rectangle Style="{StaticResource rectTop}" />
  7. <TextBlock x:Name="TBextent" Margin="20,15,15,0" Text="范围:" TextWrapping="Wrap" FontWeight="Bold" />
  8. </Canvas>
  9. </Grid>

复制代码

有 关xaml中详细的布局知识请大家参照其他例子学习,这里稍作讲解。外面的Gridright这个Grid就是页面右边1、2、3、6的父容器,之所以不 用StackPanel是因为6需要贴着页面底部,StackPanel中的元素都会flow贴到一起。三个矩形组合便构成了整体轮廓,由于它们都在一个 Canvas中,所以会产生压盖效果。最先加入的rectBottom这个矩形便是最底下的阴影效果,中间的矩形是蓝色框,最上面的矩形是白色的文字显示 区域。“{ }”里的内容在xaml中称作markupextention,StaticResource是使用在别处已经定义好的资源(resource)来对本元 素的一些属性进行自动赋值,这里用来修饰Rectangle的外观。xaml中除了StaticResource这种markupextention之外 还有Binding和TemplateBinding两种markup extention,分别用于数据绑定(databinding)和自定义control的外观。上面的StaticResource是在 App.xaml中定义的,这样就可以在本工程的任何页面中使用,当然也可以定义为LayoutRoot这个Grid的Resource。贴出来大家一看 就明白了:

  1. <Application.Resources>
  2. <Style x:Key="rectBottom" TargetType="Rectangle">
  3. <Setter Property="RadiusX" Value="10" />
  4. <Setter Property="RadiusY" Value="10" />
  5. <Setter Property="Fill" Value="#22000000" />
  6. <Setter Property="Canvas.Left" Value="5" />
  7. <Setter Property="Canvas.Top" Value="5" />
  8. <Setter Property="Width" Value="215" />
  9. <Setter Property="Height" Value="110" />
  10. </Style>
  11. <Style x:Key="rectMiddle" TargetType="Rectangle">
  12. <Setter Property="RadiusX" Value="10" />
  13. <Setter Property="RadiusY" Value="10" />
  14. <Setter Property="Fill" Value="#775C90B2" />
  15. <Setter Property="Canvas.Left" Value="0" />
  16. <Setter Property="Canvas.Top" Value="0" />
  17. <Setter Property="Width" Value="215" />
  18. <Setter Property="Height" Value="110" />
  19. <Setter Property="Stroke" Value="Gray" />
  20. </Style>
  21. <Style x:Key="rectTop" TargetType="Rectangle">
  22. <Setter Property="RadiusX" Value="5" />
  23. <Setter Property="RadiusY" Value="5" />
  24. <Setter Property="Fill" Value="#FFFFFFFF" />
  25. <Setter Property="Canvas.Left" Value="10" />
  26. <Setter Property="Canvas.Top" Value="10" />
  27. <Setter Property="Width" Value="195" />
  28. <Setter Property="Height" Value="90" />
  29. <Setter Property="Stroke" Value="DarkGreen" />
  30. </Style>
  31. </Application.Resources>

复制代码

它们就相当于网页中的css。如果不使用StaticResource,那么三个矩形看起来应该是这样的:

  1. <Rectangle RadiusX="10" RadiusY="10" Fill="#22000000" Canvas.Left="5" Canvas.Top="5" Width="215" Height="110" />
  2. <Rectangle RadiusX="10" RadiusY="10" Fill="#775C90B2" Canvas.Left="0" Canvas.Top="0" Width="215" Height="110" Stroke="Gray" />
  3. <Rectangle RadiusX="5" RadiusY="5" Fill="#FFFFFFFF" Canvas.Left="10" Canvas.Top="10" Width="195" Height="90" Stroke="DarkGreen" />

复制代码

你猜的没错,在其他矩形框部分也使用到了这些属性。通过实践可以感受到,xaml中的布局在一般使用中比html+css的布局要简单和灵活许多。好了,继续。
        Map控件里面已经封装了一些事件来供我们使用,我们可以在需要的时候捕获它们来进行处理。如果做过ArcGIS产品的二次开发,你应该已经想到我 们要捕获的就是Map的ExtentChanged事件;而要在地图移动或者缩放的过程中也实时显示地图范围,则还要对ExtentChanging事件 做处理。细心的你可能已经发现,在上面的xaml代码中已经对世界地图这个图层的Initialized事件添加了一个 hanlder:WorldImageLayer_Initialized。当然可以像这样一样给Map的这两个事件添加handler,但这里并不这么 做,而是在世界地图图层的Initialized事件里来绑定它们(移动地图时出发ExtentChanged事件,网速过慢导致图层并未加入到Map 中,则会报错)。来看看Page.xaml.cs中的code-behind代码:

  1. private void WorldImageLayer_Initialized(object sender, EventArgs e)
  2. {
  3. Map1.ExtentChanged += new EventHandler<ESRI.ArcGIS.ExtentEventArgs>(Map1_ExtentChange);
  4. Map1.ExtentChanging += new EventHandler<ESRI.ArcGIS.ExtentEventArgs>(Map1_ExtentChange);
  5. }

复制代码

没错,把两个事件绑定到同一个handler即可。再看看Map1_ExtentChange中的代码:

  1. private void Map1_ExtentChange(object sender, ESRI.ArcGIS.ExtentEventArgs e)
  2. {
  3. TBextent.Text = string.Format("地图范围:\nMinX:{0}\nMinY:{1}\nMaxX:{2}\nMaxY:{3}",
  4. e.NewExtent.XMin, e.NewExtent.YMin, e.NewExtent.XMax, e.NewExtent.YMax);
  5. }

复制代码

很简单吧?顺便提一下,ExtentEventArgs里既然有NewExtent,当然就有OldExtent了,通过比较这两个变量就可以分析出当前进行的是放大、缩小还是平移操作了。其实还有个更简单的办法,查查看Map的Resolution属性吧。
对于Silverlight API中内容,是不是感觉很容易呢(当然你得做够xaml的功课才行)?那么赶快来看第二部分。

2、当鼠标移动时获取鼠标坐标。
        包括屏幕坐标和地图坐标。外观样式方面是这样的:

  1. <!--mouse coords-->
  2. <Canvas Width="215" Height="110" Margin="0,120,0,0" VerticalAlignment="Top">
  3. <Rectangle Style="{StaticResource rectBottom}" />
  4. <Rectangle Style="{StaticResource rectMiddle}" />
  5. <Rectangle Style="{StaticResource rectTop}" />
  6. <StackPanel Orientation="Vertical" Margin="20,15,15,0">
  7. <TextBlock x:Name="TBscreencoords"
  8. HorizontalAlignment="Left" VerticalAlignment="Center" Text="屏幕坐标:" TextWrapping="Wrap" FontWeight="Bold" />
  9. <TextBlock x:Name="TBmapcoords"
  10. HorizontalAlignment="Left" VerticalAlignment="Center" Text="地图坐标:" TextWrapping="Wrap" FontWeight="Bold" />
  11. </StackPanel>
  12. </Canvas>

复制代码

那 么接下来要捕捉那个事件呢?当然就是MouseMove啦。不过如果查看SilverlightAPI中的Map类,发现并没有这个事件。但要记住Map 是继承自xaml中的Control,Control继承自FrameworkElement,FrameworkElement继承自 UIElement,这里就有一个MouseMove事件了。所以Map控件的MouseMove是xaml中而不是Siverlight API中的事件(当然整个SilverlightAPI都是建立在xaml基础上的)。在esri:Map标签中添加一个MouseMove事件 (MouseMove="Map1_MouseMove"),来看看code-behind代码:

  1. private void Map1_MouseMove(object sender, MouseEventArgs e)
  2. {
  3. if (Map1.Extent != null)
  4. {
  5. System.Windows.Point screenPnt = e.GetPosition(Map1);
  6. TBscreencoords.Text = string.Format("屏幕坐标:\nX:{0},Y:{1}", screenPnt.X, screenPnt.Y);
  7. ESRI.ArcGIS.Geometry.MapPoint mapPnt = Map1.ScreenToMap(screenPnt);
  8. TBmapcoords.Text = string.Format("地图坐标:\nX:{0}\nY:{1}", Math.Round(mapPnt.X, 4), Math.Round(mapPnt.Y, 4));
  9. }
  10. }

复制代码

可 以看到Map控件提供了屏幕与地图坐标之间转换的方法,好比开发人员的一座桥梁,用来往返于Silverlight特性与地图之间,非常方便。需要说明的 是,这里GetPosition(Map1)获得的屏幕坐标是相对于Map控件的,而不是显示器的左上角。ok,继续来看第三部分。

3、Map里的动画效果。
        当地图放大和平移时都可以看到平滑的效果,这归功于Silverlight的动画功能。Map在封装完动画效果后,给了我们两个属性来对它们进行设 置:PanDuration和ZoomDuration,用于设置这两个动作持续的时间。它们都是TimeSpan类型的变量,合理的设置可以带来良好的 用户体验。看看这部分的布局:

  1. <!--map animation slider-->
  2. <Canvas Width="215" Height="130" Margin="0,240,0,0" VerticalAlignment="Top">
  3. <Rectangle Style="{StaticResource rectBottom}" Height="130" />
  4. <Rectangle Style="{StaticResource rectMiddle}" Height="130" />
  5. <Rectangle Style="{StaticResource rectTop}" Height="110" />
  6. <StackPanel Orientation="Vertical" Margin="20,15,15,0">
  7. <TextBlock HorizontalAlignment="Left" Text="设置地图缩放动作持续时间:" TextWrapping="Wrap" FontWeight="Bold" />
  8. <TextBlock x:Name="TBzoomdurationvalue" HorizontalAlignment="Left" Text="当前值:" TextWrapping="Wrap" FontWeight="Bold" />
  9. <Slider x:Name="sliderzoomanimation" Orientation="Horizontal" Minimum="0" Maximum="20" SmallChange="1"
  10. LargeChange="5" Cursor="Hand" ValueChanged="slideranimation_ValueChanged" Width="180" />
  11. <TextBlock HorizontalAlignment="Left" Text="设置地图平移动作持续时间:" TextWrapping="Wrap" FontWeight="Bold" />
  12. <TextBlock x:Name="TBpandurationvalue" HorizontalAlignment="Left" Text="当前值:" TextWrapping="Wrap" FontWeight="Bold" />
  13. <Slider x:Name="sliderpananimation" Orientation="Horizontal" Minimum="0" Maximum="20" SmallChange="1"
  14. LargeChange="5" Cursor="Hand" ValueChanged="slideranimation_ValueChanged" Width="180" />
  15. </StackPanel>
  16. </Canvas>

复制代码

主要用到了两个slider控件。再看看拖动滑块时的事件代码,为了省事,这两个事件也用了同一个handler:

  1. private void slideranimation_ValueChanged(object sender, RoutedPropertyChangedEventArgs e)
  2. {
  3. Slider s=sender as Slider;
  4. if (s.Name == "sliderzoomanimation")
  5. {
  6. Map1.ZoomDuration = new TimeSpan(0, 0, Convert.ToInt32(sliderzoomanimation.Value));
  7. TBzoomdurationvalue.Text = string.Format("当前值:{0}秒", Convert.ToInt32(sliderzoomanimation.Value));
  8. }
  9. else
  10. {
  11. Map1.PanDuration = new TimeSpan(0, 0, Convert.ToInt32(sliderpananimation.Value));
  12. TBpandurationvalue.Text = string.Format("当前值:{0}秒", Convert.ToInt32(sliderpananimation.Value));
  13. }
  14. }

复制代码

对应着地图效果,应该很容易理解。继续第四部分。

4、对地图服务可见性与动态地图服务中图层可见性的控制。
        还是要强调一下,WorldImagery和StreetMap两个能看到的地图实际上都是地图服务,当作layer加入到了Map控件中;而动态 地图服务USA中的图层Cities,Rivers,States才是与ArcMap中图层相对的概念。对于WorldImagery和 StreetMap之间的切换,主要用到了Silverlight API里Layer的
Visible属性;而动态服务中图层可见性的操作,主要是对ArcGISDynamicMapServiceLayer的VisibleLayers数组做了设置。
        StreetMap这个服务其实一开始就加入了地图(在esri:Map标签中):

  1. <esri:ArcGISTiledMapServiceLayer ID="StreetMapLayer"
  2. Url="http://services.arcgisonline.com/ArcGIS/rest/services/ESRI_StreetMap_World_2D/MapServer" Visible="False" />

复制代码

而设置了Visible="False"。图层不可见时地图不会对它做任何处理,所以不用担心会耗费流量或加重程序负担。
        看看布局部分:

  1. <StackPanel HorizontalAlignment="Left" Margin="20,15,0,0">
  2. <Canvas x:Name="Canvasleft" Width="165" Height="90" HorizontalAlignment="Left" VerticalAlignment="Top">
  3. <Rectangle Style="{StaticResource rectBottom}" Width="165" Height="90" />
  4. <Rectangle Style="{StaticResource rectMiddle}" Fill="#7758FF00" Width="165" Height="90" />
  5. <Rectangle Style="{StaticResource rectTop}" Width="145" Height="70" />
  6. <!--change layer-->
  7. <StackPanel Margin="20,15,15,0">
  8. <TextBlock Text="切换图层:" TextWrapping="Wrap" FontWeight="Bold" />
  9. <StackPanel Orientation="Horizontal">
  10. <ToggleButton x:Name="TBimagery" Content="Imagery" Click="TBimagery_Clicked" Cursor="Hand" />
  11. <ToggleButton x:Name="TBstreetmap" Content="StreetMap" Click="TBstreetmap_Clicked" Cursor="Hand" />
  12. </StackPanel>
  13. <CheckBox Margin="0,5,0,0" x:Name="chkboxDynamicLayer" Content="添加一个动态图层吧" IsChecked="False" Click="chkboxDynamicLayer_Click" Cursor="Hand" />
  14. </StackPanel>
  15. </Canvas>
  16. </StackPanel>

复制代码

这 里使用了ToggleButton,CheckBox和RadioButton都由它派生而来。Silverlight2中的ToggleButton不 能设置Group(一个Group中自动限定同时只能有一个控件处于激活状态),不如Flex里的ToggleButton来的方便,所以code- behind中多做了些工作。当然这里使用RadioButton也是可以的。

  1. private void TBimagery_Clicked(object sender, RoutedEventArgs e)
  2. {
  3. if (TBstreetmap.IsChecked==true)
  4. {
  5. Map1.Layers["WorldImageLayer"].Visible = true;
  6. Map1.Layers["WorldImageLayer"].Opacity = 0;
  7. TBstreetmap.IsChecked = false;
  8. Storyboard sbworldmapshow = makestoryboard("WorldImageLayer", 0, 1);
  9. Storyboard sbstreetmaphide = makestoryboard("StreetMapLayer", 1, 0);
  10. sbworldmapshow.Begin();
  11. sbstreetmaphide.Begin();
  12. hidelayername = "StreetMapLayer";
  13. timer.Begin();
  14. }
  15. TBimagery.IsChecked = true;
  16. }
  17. private void TBstreetmap_Clicked(object sender, RoutedEventArgs e)
  18. {
  19. if (TBimagery.IsChecked==true)
  20. {
  21. Map1.Layers["StreetMapLayer"].Visible = true;
  22. Map1.Layers["StreetMapLayer"].Opacity = 0;
  23. TBimagery.IsChecked = false;
  24. Storyboard sbstreetmapshow = makestoryboard("StreetMapLayer", 0, 1);
  25. Storyboard sbworldmaphide = makestoryboard("WorldImageLayer", 1, 0);
  26. sbstreetmapshow.Begin();
  27. sbworldmaphide.Begin();
  28. hidelayername = "WorldImageLayer";
  29. timer.Begin();
  30. }
  31. TBstreetmap.IsChecked = true;
  32. }
  33. private void timer_Tick(object sender, EventArgs e)
  34. {
  35. Map1.Layers[hidelayername].Visible = false;
  36. }
  37. public Storyboard makestoryboard(string layername, double from, double to)
  38. {
  39. Storyboard sb = new Storyboard();
  40. ESRI.ArcGIS.ArcGISTiledMapServiceLayer layer = Map1.Layers[layername] as ESRI.ArcGIS.ArcGISTiledMapServiceLayer;
  41. DoubleAnimation doubleAnim = new DoubleAnimation();
  42. doubleAnim.Duration = new TimeSpan(0, 0, 5);
  43. doubleAnim.From = from;
  44. doubleAnim.To = to;
  45. Storyboard.SetTarget(doubleAnim, layer);
  46. Storyboard.SetTargetProperty(doubleAnim, new PropertyPath("Opacity"));
  47. sb.Children.Add(doubleAnim);
  48. return sb;
  49. }

复制代码

当 切换两个地图服务时能够看到一个渐变的效果,这里用到了Silverlight中的动画,它们都是在StoryBoard里面进行的,以后的小节中会讲 Silverlight中的动画,这里不再废话了,有兴趣的朋友可以自己参考帮助学习。hidelayername是这个一个公用的string变量,用 来在切换的动画效果完成后设置不可见的图层Visible属性。timer也是一个StoryBoard:

  1. <Storyboard x:Name="timer" Completed="timer_Tick" Duration="0:0:5" />

复制代码

这里可以看出把StoryBoard也能巧妙的用作计时器。到了特定时间(5秒)后会自动timer_Tick函数,当然也可以使用.net中的各种timer类。
        下面是添加动态服务的部分。

  1. private void chkboxDynamicLayer_Click(object sender, RoutedEventArgs e)
  2. {
  3. if (chkboxDynamicLayer.IsChecked == true)
  4. {
  5. Map1.Layers.Add(california);
  6. Map1.ZoomTo(california.FullExtent);
  7. if (california.IsInitialized == false)
  8. {
  9. chkboxDynamicLayer.IsEnabled = false;
  10. }
  11. chkboxDynamicLayer.Content = "去掉它";
  12. SVlayers.Visibility = Visibility.Visible;
  13. }
  14. else
  15. {
  16. Map1.Layers.Remove(california);
  17. chkboxDynamicLayer.Content = "添加一个动态图层吧";
  18. SVlayers.Visibility = Visibility.Collapsed;
  19. }
  20. }
  21. private void dynamiclayer_initialized(object s, EventArgs e)
  22. {
  23. //若图层没有初始化好就移除图层,当然会报错了,所以这样做就不会了
  24. chkboxDynamicLayer.IsEnabled = true;
  25. Map1.ZoomTo(california.InitialExtent);
  26. SVlayers.Visibility = Visibility.Visible;
  27. california.ID = "layercalifornia";
  28. ESRI.ArcGIS.ArcGISDynamicMapServiceLayer dynamicServiceLayer = s as ESRI.ArcGIS.ArcGISDynamicMapServiceLayer;
  29. if (dynamicServiceLayer.VisibleLayers == null)
  30. dynamicServiceLayer.VisibleLayers = GetDefaultVisibleLayers(dynamicServiceLayer);
  31. UpdateLayerList(dynamicServiceLayer);
  32. }

复制代码

当添加了动态服务后,会自动弹出一个listbox,当然这些也都是在xaml中定义好的(加在上面的Canvas后面):

  1. <ScrollViewer x:Name="SVlayers" Width="165" Visibility="Collapsed" Height="120">
  2. <ListBox x:Name="LayerVisibilityListBox" >
  3. <ListBox.ItemTemplate>
  4. <DataTemplate>
  5. <CheckBox Margin="2" Name="{Binding LayerIndex}" Content="{Binding LayerName}"
  6. Tag="{Binding ServiceName}" IsChecked="{Binding Visible}"
  7. ClickMode="Press" Click="chkboxToggleVilible_Click" />
  8. </DataTemplate>
  9. </ListBox.ItemTemplate>
  10. </ListBox>
  11. </ScrollViewer>

复制代码

这 里把ListBox放到了ScrollVierwer中,固定了它的高度,当内容过多时可以自动显示纵向滚动条。这里要提一下,ListBox的内容用到 了数据绑定(参考xaml中的DataBinding,有OneTime,OneWay和TwoWay三种模式,这里使用的是默认的OneWay),看起 来里面只有一个CheckBox,但它相当于一个模板,在code-behind中设置了ListBox.ItemSource之后,根据该属性的内容自 动生成多个CheckBox。代码中自定义了一个LayerListData类,它的几个属性分别与上面的CheckBox属性绑定;将一个List赋给 了ListBox.ItemSource,则会自动生成ListBox中的内容。通过一个List类型变量,来控制动态服务的可见图层。代码如下:

  1. public class LayerListData
  2. {
  3. public bool Visible { get; set; }
  4. public string ServiceName { get; set; }
  5. public string LayerName { get; set; }
  6. public int LayerIndex { get; set; }
  7. }
  8. private int[] GetDefaultVisibleLayers(ESRI.ArcGIS.ArcGISDynamicMapServiceLayer dynamicService)
  9. {
  10. List<int> visibleLayerIDList = new List<int>();
  11. ESRI.ArcGIS.LayerInfo[] layerInfoArray = dynamicService.Layers;
  12. for (int index = 0; index < layerInfoArray.Length; index++)
  13. {
  14. if (layerInfoArray[index].DefaultVisibility)
  15. visibleLayerIDList.Add(index);
  16. }
  17. return visibleLayerIDList.ToArray();
  18. }
  19. private void UpdateLayerList(ESRI.ArcGIS.ArcGISDynamicMapServiceLayer dynamicServiceLayer)
  20. {
  21. int[] visibleLayerIDs = dynamicServiceLayer.VisibleLayers;
  22. if (visibleLayerIDs == null)
  23. visibleLayerIDs = GetDefaultVisibleLayers(dynamicServiceLayer);
  24. List<LayerListData> visibleLayerList = new List<LayerListData>();
  25. ESRI.ArcGIS.LayerInfo[] layerInfoArray = dynamicServiceLayer.Layers;
  26. for (int index = 0; index < layerInfoArray.Length; index++)
  27. {
  28. visibleLayerList.Add(new LayerListData()
  29. {
  30. Visible = visibleLayerIDs.Contains(index),
  31. ServiceName = dynamicServiceLayer.ID,
  32. LayerName = layerInfoArray[index].Name,
  33. LayerIndex = index
  34. });
  35. }
  36. LayerVisibilityListBox.ItemsSource = visibleLayerList;
  37. }
  38. void chkboxToggleVilible_Click(object sender, RoutedEventArgs e)
  39. {
  40. CheckBox tickedCheckBox = sender as CheckBox;
  41. string serviceName = tickedCheckBox.Tag.ToString();
  42. bool visible = (bool)tickedCheckBox.IsChecked;
  43. int layerIndex = Int32.Parse(tickedCheckBox.Name);
  44. ESRI.ArcGIS.ArcGISDynamicMapServiceLayer dynamicServiceLayer = Map1.Layers[serviceName] as
  45. ESRI.ArcGIS.ArcGISDynamicMapServiceLayer;
  46. List<int> visibleLayerList =
  47. dynamicServiceLayer.VisibleLayers != null
  48. ? dynamicServiceLayer.VisibleLayers.ToList() : new List<int>();
  49. if (visible)
  50. {
  51. if (!visibleLayerList.Contains(layerIndex))
  52. visibleLayerList.Add(layerIndex);
  53. }
  54. else
  55. {
  56. if (visibleLayerList.Contains(layerIndex))
  57. visibleLayerList.Remove(layerIndex);
  58. }
  59. dynamicServiceLayer.VisibleLayers = visibleLayerList.ToArray();
  60. }

复制代码

5、比例尺。
        Silverlight API提供了一个ScaleBar类,可以方便的设置地图比例尺。

  1. <!--scale bar 放在LayoutRoot Grid中-->
  2. <Canvas HorizontalAlignment="Left" VerticalAlignment="Bottom" Margin="10,0,0,20">
  3. <esri:ScaleBar x:Name="scalebar" MapUnit="DecimalDegrees" DisplayUnit="Kilometers" Foreground="Black" FillColor1="White" FillColor2="Blue" />
  4. </Canvas>

复制代码

需要在初始化的时候设置scalebar的Map属性,顺便来看看整个页面的初始化工作:

  1. namespace demo_02_extendedmap
  2. {
  3. public partial class Page : UserControl
  4. {
  5. private ESRI.ArcGIS.ArcGISDynamicMapServiceLayer california = new ESRI.ArcGIS.ArcGISDynamicMapServiceLayer();
  6. private string hidelayername;
  7. public Page()
  8. {
  9. InitializeComponent();
  10. scalebar.Map = Map1;
  11. scalebarstoryboard.Begin();
  12. TBzoomdurationvalue.Text = string.Format("当前值:{0}.{1}秒", Map1.ZoomDuration.Seconds, Map1.ZoomDuration.Milliseconds);
  13. TBpandurationvalue.Text = string.Format("当前值:{0}.{1}秒", Map1.PanDuration.Seconds, Map1.PanDuration.Milliseconds);
  14. california.Url = "http://sampleserver1.arcgisonline.com/ArcGIS/rest/services/Specialty/ESRI_StatesCitiesRivers_USA/MapServer";
  15. california.Opacity = 0.5;
  16. california.Initialized += new EventHandler<EventArgs>(dynamiclayer_initialized);
  17. TBimagery.IsChecked = true;
  18. makestoryboard("WorldImageLayer", 0, 1).Begin();
  19. //切换全屏/窗口
  20. Application.Current.Host.Content.FullScreenChanged += new EventHandler(fullscreen_changed);
  21. }
  22. }
  23. }

复制代码

scalebarstoryboard是xaml里自定义的一个动画,效果见比例尺旁的单位。

6、地图相关操作。
        Map控件已经内置了一些键盘鼠标事件,但目前不能像JavascriptAPI中那样禁用这些事件。这里还用到了Silverlight程序的一 个全屏特性,其实是对Application.Current.Host.Content的一个属性做了设置。直接看代码吧:

  1. <!--operation info-->
  2. <Canvas Width="215" Height="110" Margin="0,0,0,30" VerticalAlignment="Bottom">
  3. <Rectangle Style="{StaticResource rectBottom}" />
  4. <Rectangle Style="{StaticResource rectMiddle}" Fill="#77FF0000" />
  5. <Rectangle Style="{StaticResource rectTop}" />
  6. <TextBlock Margin="20,15,15,0" TextWrapping="Wrap"
  7. Text="地图操作提示:双击放大 Shift+拖拽:放大到指定范围 Ctrl+Shift+拖拽:缩小到指定范围" />
  8. <ToggleButton x:Name="TBfullscreen" Content="点击切换地图全屏" HorizontalAlignment="Center" Canvas.Left="100" Canvas.Top="15" Height="30" Click="TBfullscreen_Click" />
  9. </Canvas>

复制代码

放到Gridright Grid中,

  1. private void TBfullscreen_Click(object sender, RoutedEventArgs e)
  2. {
  3. System.Windows.Interop.Content content = Application.Current.Host.Content;
  4. content.IsFullScreen=!content.IsFullScreen;
  5. }
  6. private void fullscreen_changed(object o,EventArgs e)
  7. {
  8. System.Windows.Interop.Content content=Application.Current.Host.Content;
  9. TBfullscreen.IsChecked = content.IsFullScreen;
  10. }

复制代码

7、进度条。
        最后还剩下地图中的这个进度条。利用了Map控件内置的一个Progress事件。

  1. <!--progressbar 放在LayoutRoot中-->
  2. <Grid HorizontalAlignment="Center" x:Name="progressGrid" VerticalAlignment="Center" Width="200" Height="20" Margin="5,5,5,5">
  3. <ProgressBar x:Name="MyProgressBar" Minimum="0" Maximum="100" />
  4. <TextBlock x:Name="ProgressValueTextBlock" Text="100%" HorizontalAlignment="Center" VerticalAlignment="Center" />
  5. </Grid>

复制代码

在esri:Map标签中加入一个事件:Progress="Map1_Progress",

  1. private void Map1_Progress(object sender, ESRI.ArcGIS.ProgressEventArgs e)
  2. {
  3. if (e.Progress < 100)
  4. {
  5. progressGrid.Visibility = Visibility.Visible;
  6. MyProgressBar.Value = e.Progress;
  7. ProgressValueTextBlock.Text = String.Format("正在处理 {0}%", e.Progress);
  8. }
  9. else
  10. {
  11. progressGrid.Visibility = Visibility.Collapsed;
  12. }
  13. }

复制代码

好了到此就已经讲完了整个地图功能。尽管想尽可能详细说明每段代码,便于初学的朋友学习,但也不可能面面俱到。没有讲明白的地方大家可以自己思考,查帮助。学习的过程中,不思考,无进步。
原文地址:http://bbs.esrichina-bj.cn/ESRI/thread-44365-1-1.html

ArcGIS API for Silverlight开发入门(3):Widgets

Widgets翻译过来是小玩具。如果使用过Dojo或者ExtJS等js框架肯定会了解到这个“小玩具”也有大用处,能够在很大程度上减少我们的工作量,快速完成功能需求。能减少多大工作量呢?让我们先来,点击这里,看一个例子。

前两节的地图中,总感觉少点什么……对,就是一个sliderbar,有了它感觉就像汽车有了方向盘一样,能够控制方向了。那么来看看实现上面这个例子中的滑块条需要做什么工作吧。
在silverlight中创建一个UserControl,把上面sliderbar的外观和功能都封装在里面。
来看具体工作。vs中,在silverlight工程上右键单击,add,new item,选择silverlight user control,起名叫mapslider,在mapslider.xaml中填如下代码:

  1. <Grid x:Name="slidergrid" HorizontalAlignment="Left" VerticalAlignment="Center" Background="Azure" Margin="20">
  2. <StackPanel Orientation="Vertical">
  3. <Button x:Name="btnzoomin" Content="+" Click="btnzoomin_Click" />
  4. <Slider x:Name="sliderLOD" Orientation="Vertical"  Height="200" SmallChange="1" LargeChange="1"  Minimum="0" Cursor="Hand" ValueChanged="slider1_ValueChanged" />
  5. <Button x:Name="btnzoomout" Content="-" Click="btnzoomout_Click" />
  6. </StackPanel>
  7. </Grid>

复制代码

上 面这些就是滑块条的外观,接下来看功能部分。大致思路是在mapslider类中设置一个公共属性Map,就是需要操作的地图了,但这个属性不是 ESRI.ArcGIS.Map,而是另一个自定义类。为什么要这么做?因为这个自定义类需要实现INotifyPropertyChanged接口,当 我们把自己的Map控件作为mapslider的属性赋值的时候,这个Map需要做另外一些工作。看代码吧,不太明白的话就要加强对 silverlight中data binding的学习。在mapslider.xaml.cs页面中填入一下代码:

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Net;
  5. using System.Windows;
  6. using System.Windows.Controls;
  7. using System.Windows.Documents;
  8. using System.Windows.Input;
  9. using System.Windows.Media;
  10. using System.Windows.Media.Animation;
  11. using System.Windows.Shapes;
  12. using System.ComponentModel;
  13. namespace customcontrol
  14. {
  15. public partial class mapslider : UserControl
  16. {
  17. private mymap map = new mymap();
  18. public ESRI.ArcGIS.Map Map
  19. {
  20. get
  21. {
  22. return map.Map;
  23. }
  24. set
  25. {
  26. map.Map=value;
  27. if (map.Map != null)
  28. {
  29. Map.ExtentChanged += new EventHandler<ESRI.ArcGIS.ExtentEventArgs>(map_ExtentChanged);
  30. Map.SnapToLevels = true;
  31. ((ESRI.ArcGIS.ArcGISTiledMapServiceLayer)Map.Layers[0]).Initialized += new EventHandler<EventArgs>(layer0_initialized);
  32. }
  33. }
  34. }
  35. private void layer0_initialized(object o,EventArgs e)
  36. {
  37. sliderLOD.Maximum = ((ESRI.ArcGIS.ArcGISTiledMapServiceLayer)Map.Layers[0]).TileInfo.Lods.Length - 1;
  38. }
  39. public mapslider()
  40. {
  41. InitializeComponent();
  42. }
  43. private void slider1_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
  44. {
  45. if (map.Map!=null)
  46. {
  47. Map.ZoomToResolution(((ESRI.ArcGIS.ArcGISTiledMapServiceLayer)Map.Layers[0]).TileInfo.Lods[Convert.ToInt32(e.NewValue)].Resolution);
  48. }
  49. }
  50. private void map_ExtentChanged(object o, ESRI.ArcGIS.ExtentEventArgs e)
  51. {
  52. ESRI.ArcGIS.ArcGISTiledMapServiceLayer layer = Map.Layers[0] as ESRI.ArcGIS.ArcGISTiledMapServiceLayer;
  53. int i;
  54. for (i = 0; i < layer.TileInfo.Lods.Length; i++)
  55. {
  56. if (Map.Resolution == layer.TileInfo.Lods[i].Resolution)
  57. break;
  58. }
  59. sliderLOD.Value = i;
  60. }
  61. private void btnzoomin_Click(object sender, RoutedEventArgs e)
  62. {
  63. sliderLOD.Value += 1;
  64. }
  65. private void btnzoomout_Click(object sender, RoutedEventArgs e)
  66. {
  67. sliderLOD.Value -= 1;
  68. }
  69. }
  70. //执行了这个接口后,当在主页面page.xaml.cs中给Map赋值的时候,就能返到set语句中,以便执行绑定事件的代码
  71. public class mymap:INotifyPropertyChanged
  72. {
  73. private ESRI.ArcGIS.Map map;
  74. public ESRI.ArcGIS.Map Map
  75. {
  76. get{return map;}
  77. set
  78. {
  79. map = value;
  80. if (PropertyChanged!=null)
  81. {
  82. PropertyChanged(this, new PropertyChangedEventArgs("Map"));
  83. }
  84. }
  85. }
  86. public event PropertyChangedEventHandler PropertyChanged;
  87. }
  88. }

复制代码

做完封装的工作,来看如何在page.xaml中使用这个控件。只需要三行代码:1、注册user control的命名空间(和对Silverlight API的引用是一样的,放在页面中的根元素UserControl里):
xmlns:uc="clr-namespace:customcontrol"
        2、在页面中添加这个slider:
<Grid x:Name="LayoutRoot" Background="White">
        <!--地图在这里-->
        </esri:Map>
        
        <uc:mapslider x:Name="mapslider1"/>
</Grid>
        3、在初始化的时候对我们自定义控件的Map属性赋值(page.xaml.cs中):
public Page()
      {
          InitializeComponent();
          mapslider1.Map = Map1;
      }
        到此应该有这个感觉,封装比较麻烦,但使用封装好的控件非常简便。这就是Widgets带给我们的好处。目前的beta版 中,SilverlightAPI已经替我们完成5个Widgets的封装,它们分别 是:Magnifier,ToolBar,BookMark,Navigation,MapTip,其中ToolBar内部使用了 ToolBarItemCollection和ToolBarItem等类。还是通过一个例子,来看看这几个控件都长什么样吧(点击这里):

MapTip需要使用到Query Task,以后的小节中再涉及到。现在分别熟悉一下这几个Widgets的用法。
1、ToolBar和Magnifier:
        这个和ADF开发中的ToolBar(工具条)是一样的,里面可以添加ToolItem(工具),已实现各种功能,比如平移,缩放等。 silverlight中当然要有一些比较好看的效果了,比如把鼠标放在工具条上选择工具的时候,会有放大效果,这个效果是默认的,不能设置;点击一个工 具时,该工具会跳动一下,这个是ToolbarItemClickEffect中的Bounce效果(目前只有Bounce和None两个选择),也是默 认的。此例中ToolBar里面有三个ToolBarItem,分别是Pan,FullExtent和Magnifier(本身也是一个Widget), 下面是ToolBar的布局:

  1. <Grid Height="110" HorizontalAlignment="Right" VerticalAlignment="Top" Margin="0,10,10,0" >
  2. <Rectangle Fill="#22000000" RadiusX="10" RadiusY="10" Margin="0,4,0,0" />
  3. <Rectangle Fill="#775C90B2" Stroke="Gray"  RadiusX="10" RadiusY="10" Margin="0,0,0,5" />
  4. <Rectangle Fill="#66FFFFFF" Stroke="DarkGray" RadiusX="5" RadiusY="5" Margin="10,10,10,15" />
  5. <StackPanel Orientation="Vertical">
  6. <esriWidgets:Toolbar x:Name="MyToolbar" MaxItemHeight="80" MaxItemWidth="80"
  7. VerticalAlignment="Top" HorizontalAlignment="Center"
  8. ToolbarItemClicked="MyToolbar_ToolbarItemClicked"
  9. ToolbarItemClickEffect="Bounce"
  10. Width="250" Height="80">
  11. <esriWidgets:Toolbar.Items>
  12. <esriWidgets:ToolbarItemCollection>
  13. <esriWidgets:ToolbarItem Text="Pan">
  14. <esriWidgets:ToolbarItem.Content>
  15. <Image Source="img/i_pan.png" Stretch="UniformToFill" Margin="5" />
  16. </esriWidgets:ToolbarItem.Content>
  17. </esriWidgets:ToolbarItem>
  18. <esriWidgets:ToolbarItem Text="Full Screen">
  19. <esriWidgets:ToolbarItem.Content>
  20. <Image Source="img/i_globe.png" Stretch="UniformToFill" Margin="5" />
  21. </esriWidgets:ToolbarItem.Content>
  22. </esriWidgets:ToolbarItem>
  23. <esriWidgets:ToolbarItem Text="Full Screen">
  24. <esriWidgets:ToolbarItem.Content>
  25. <Image Source="img/magglass.png" Stretch="UniformToFill" Margin="5"
  26. MouseLeftButtonDown="Image_MouseLeftButtonDown"/>
  27. </esriWidgets:ToolbarItem.Content>
  28. </esriWidgets:ToolbarItem>
  29. </esriWidgets:ToolbarItemCollection>
  30. </esriWidgets:Toolbar.Items>
  31. </esriWidgets:Toolbar>
  32. <TextBlock x:Name="StatusTextBlock" Text="" FontWeight="Bold" HorizontalAlignment="Center"/>
  33. </StackPanel>
  34. </Grid>

复制代码

然后是code-behind内容:
private void MyToolbar_ToolbarItemClicked(object sender, ESRI.ArcGIS.Widgets.SelectedToolbarItemArgs e)
        {
            switch (e.Index)
            {
                case 0:
                    //pan
                    break;
                case 1:
                    Map1.ZoomTo(Map1.Layers.GetFullExtent());
                    break;
                case 2:
                    break;
            }
        }

private void Image_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            MyMagnifier.Enabled = !MyMagnifier.Enabled;
        }
        别忘了在page的构造函数中加一句:MyMagnifier.Initialize(Map1);。可以看出,Pan工具不需要任何代码,因为地 图本身的默认动作就是Pan,而FullExtent也是利用了Map的ZoomTo()。放大镜的工具是在该图片被鼠标左键按住的过程中激活的(设置 enabled属性),只要鼠标左键没有按住放大镜图片,该Widget就设置为不可用。比较有用的是我们可以单独设置放大镜自己的图层及放大倍数,这里 放大镜使用的就是StreetMap,倍数为3。
2、BookMark:
        这个功能和ArcMap(9.3版本)中的BookMark是一样的,可以像看书一样,为当前地图范围设置一个书签,便于其他时候快速定位到该范 围。而查看API中的Bookmark.MapBookmark类(可以利用它对书签的内容进行单个添加或删除),可以发现其实每个书签存储的内容是一个 Extent,然后再起一个名字就可以了。添加了bookmark widget后似乎会造成vs中的preview窗口出错。
<!--bookmark-->
        <Canvas>
            <esriWidgets:Bookmark x:Name="MyBookmarks" Width="125" HorizontalAlignment="Left" VerticalAlignment="Top"
             Margin="20" Background="#99257194" BorderBrush="#FF92a8b3" Foreground="Black"
             Loaded="MyBookmarks_Loaded" />
        </Canvas>
page.xaml.cs中:
private void MyBookmarks_Loaded(object sender, RoutedEventArgs e)
        {
            MyBookmarks.Map = Map1;
        }
3、Navigation:
        这个导航条工具是目前网络地图必备的一个控件,但silverlight的功能,可以轻易实现地图的旋转(其实也可以在代码中通过 Map.Rotation属性来设置)。经试验这个widget只能放在StackPanel或Grid容器里,如果放在Canvas里的话地图中不会显 示。
<!--navigation bar.must be in a stackpanel-->
        <StackPanel HorizontalAlignment="Left" VerticalAlignment="Bottom">
            <esriWidgets:Navigation x:Name="MyNavigation" Margin="5"  />
        </StackPanel>
        同样在page的构造函数中添加一句:MyNavigation.Map = Map1;。
        API中的Widgets可以简化我们的工作,拿来即用。但明显的缺陷就是不灵活,如果想使自己的控件不那么千篇一律的话,就需要自己进行开发工作了。
原文地址:http://bbs.esrichina-bj.cn/ESRI/thread-44555-1-1.html

ArcGIS API for Silverlight开发入门(4):用户与地理信息之间的桥梁--GraphicsLayer

我们与地图交互的过程时刻在进行着:一个拉框放大操作,或者对地图内容的查询等。 这些交互过程中的输入输出,通常都是反映在独立于地图数据一个“层”上。比如拉框放大,我们能看见鼠标所画的一个矩形;又比如对兴趣点的查询,结果通常是 将符合条件的兴趣点的形状高亮显示在那个独立的“层”中,通过它既可以反映用户的输入,又可以展现地图的输出。这个“层”就是 GraphicsLayer。
        其实ADF开发中也有GraphicsLayer的概念,同样在其他两个客户端API(JavaScript/Flex)中也能找到GraphicsLayer的身影,它们都是一样一样的。
        本节我们主要看如何在GraphicsLayer中展现内容。当然第一个工作就是添加ESRI.ArcGIS.dll的引用,引入esri的xml命名空间;接下来在Map中添加一个GraphicsLayer图层:

  1. <esri:Map x:Name="Map1">
  2. <esri:Map.Layers>
  3. <!-- 其他图层 -->
  4. <esri:GraphicsLayer ID="GLayer" />
  5. </esri:Map.Layers>
  6. </esri:Map>

复制代码

要 使GraphicsLayer中的内容处于最顶端(不被其他图层内容覆盖),就要将它放在Map标签里的最下头,像上面那样。从命名我们不难看 出,GraphicLayer里面放的就是Graphic的集合了。Graphic(ESRI.ArcGIS.Graphic)是 GraphicsLayer中的基本元素,它包括了Geometry(在ESRI.ArcGIS.Geometry命名空间中),Symbol(在 ESRI.ArcGIS.Symbol命名空间中),Attributes等属性。所有显示在地图中的矢量元素都有一个Geometry,里面包含了若干 地理坐标,用于显示地图上地物的形状,它是Point,Polyline,Polygon等的总称,在这里代表了Graphic的形状。Symbol代表 了Graphic的外观,它是一系列符号的总称,我们通常跟SimpleMarkerSymbol,SimpleLineSymbol和 SimpleFillSymbol等打交道,它们分别对应了上面3种不同的Geometry(Point,Polyline,Polygon)。
        要让一个Graphic显示出来,总共分3步:
1、定义Graphic:
在xaml中

  1. <esri:Graphic>
  2. </esri:Graphic>

复制代码

在code-behind中
Graphic g= new Graphic()
2、设置Graphic的Geometry和Symbol属性:
在xaml中

  1. <esri:Graphic>
  2. <esri:Graphic.Symbol>
  3. <esriSymbols:SimpleMarkerSymbol Color="Blue" Size="12" Style="Square" />
  4. </esri:Graphic.Symbol>
  5. <esriGeometry:MapPoint X="108" Y="30" />
  6. </esri:Graphic>

复制代码

在code-behind中

  1. Graphic g = new Graphic()
  2. {
  3. Geometry = new MapPoint(108, 30),
  4. Symbol = new SimpleMarkerSymbol()
  5. {
  6. Color = new SolidColorBrush(Colors.Blue),
  7. Size = 12,
  8. Style = SimpleMarkerSymbol.SimpleMarkerStyle.Square
  9. }
  10. };

复制代码

3、把定义好的Graphic添加到GraphicsLayer里:
在xaml中

  1. <esri:GraphicsLayer ID="GLayer">
  2. <esri:GraphicsLayer.Graphics>
  3. <esri:Graphic>
  4. <esri:Graphic.Symbol>
  5. <esriSymbols:SimpleMarkerSymbol Color="Blue" Size="12" Style="Square" />
  6. </esri:Graphic.Symbol>
  7. <esriGeometry:MapPoint X="108" Y="30" />
  8. </esri:Graphic>
  9. </esri:GraphicsLayer.Graphics>
  10. </esri:GraphicsLayer>

复制代码

在code-behind中

  1. Graphic g = new Graphic()
  2. {
  3. Geometry = new MapPoint(108, 30),
  4. Symbol = new SimpleMarkerSymbol()
  5. {
  6. Color = new SolidColorBrush(Colors.Blue),
  7. Size = 12,
  8. Style = SimpleMarkerSymbol.SimpleMarkerStyle.Square
  9. }
  10. };
  11. GraphicsLayer glayer = Map1.Layers["GLayer"] as GraphicsLayer;
  12. glayer.Graphics.Add(g);

复制代码

看一下效果:

图中还有其他的图形,无非是改变了Graphic的Geometry和Symbol属性。图上的那只灰熊是一段动画文件,利用Silverlight的特性,能够定义出表现力丰富的各种符号。
        尽管能够完全在xaml中来完成工作,但还是建议将可视化元素的定义放在xaml中,将实现的逻辑部分放在code-behind中。看一下添加图中那些Graphic的代码:

  1. <Grid.Resources>
  2. <esriSymbols:SimpleMarkerSymbol x:Name="RedMarkerSymbol" Color="Red" Size="12" Style="Circle" />
  3. <!-- 可惜目前Silverlight只支持Jpeg和PNG格式的图像,所以PictureMarkerSymbol无法显示GIF格式的图像,否则会报ImagingError的错误 -->
  4. <esriSymbolsictureMarkerSymbol x:Name="PinPictureMarkerSymbol" Source="imgs/pin.png" OffsetX="10" OffsetY="10" />
  5. <esriSymbols:SimpleLineSymbol x:Name="RedLineSymbol" Color="Red" Width="4" Style="Solid" />
  6. <esriSymbols:CartographicLineSymbol x:Name="CartoLineSymbol" Color="Red" Width="10" DashCap="Triangle" LineJoin="Round" DashArray="6,2" />
  7. <esriSymbols:SimpleFillSymbol x:Name="RedFillSymbol" Fill="#66FF0000" BorderBrush="Red" BorderThickness="2" />
  8. </Grid.Resources>
  9. <MediaElement x:Name="BearVideo" />

复制代码

  1. private void AddGraphics()
  2. {
  3. GraphicsLayer glayer = Map1.Layers["GLayer"] as GraphicsLayer;
  4. Graphic[] graphics = new Graphic[8];
  5. graphics[0] = new Graphic()
  6. {
  7. Geometry = new MapPoint(108, 34),
  8. Symbol = RedMarkerSymbol
  9. };
  10. graphics[1] = new Graphic()
  11. {
  12. Geometry = new MapPoint(108, 30),
  13. Symbol = new SimpleMarkerSymbol()
  14. {
  15. Color = new SolidColorBrush(Colors.Blue),
  16. Size = 12,
  17. Style = SimpleMarkerSymbol.SimpleMarkerStyle.Square
  18. }
  19. };
  20. graphics[2] = new Graphic()
  21. {
  22. Geometry = new MapPoint(108, 25),
  23. Symbol = PinPictureMarkerSymbol
  24. };
  25. graphics[3] = new Graphic()
  26. {
  27. Geometry = new MapPoint(108, 20),
  28. Symbol = new TextSymbol()
  29. {
  30. FontFamily = new FontFamily("微软雅黑, 宋体"),
  31. FontSize = 14,
  32. Foreground = new SolidColorBrush(Colors.Black),
  33. Text = "这是text symbol"
  34. }
  35. };
  36. graphics[4] = new Graphic();
  37. graphics[4].Symbol = RedLineSymbol;
  38. ESRI.ArcGIS.Geometry.PointCollection pc = new ESRI.ArcGIS.Geometry.PointCollection()
  39. {
  40. new MapPoint(95,10),
  41. new MapPoint(110,-15),
  42. new MapPoint(130,10)
  43. };
  44. ESRI.ArcGIS.Geometry.Polyline pl = new ESRI.ArcGIS.Geometry.Polyline();
  45. pl.Paths.Add(pc);
  46. graphics[4].Geometry = pl;
  47. graphics[5] = new Graphic();
  48. graphics[5].Symbol = CartoLineSymbol;
  49. ESRI.ArcGIS.Geometry.PointCollection pc1 = new ESRI.ArcGIS.Geometry.PointCollection()
  50. {
  51. new MapPoint(95,0),
  52. new MapPoint(110,-25),
  53. new MapPoint(130,0)
  54. };
  55. ESRI.ArcGIS.Geometry.Polyline pl1 = new ESRI.ArcGIS.Geometry.Polyline();
  56. pl1.Paths.Add(pc1);
  57. graphics[5].Geometry = pl1;
  58. graphics[6] = new Graphic()
  59. {
  60. Symbol = RedFillSymbol
  61. };
  62. ESRI.ArcGIS.Geometry.PointCollection pc2 = new ESRI.ArcGIS.Geometry.PointCollection()
  63. {
  64. new MapPoint(110,-30),
  65. new MapPoint(130,-30),
  66. new MapPoint(130,-45),
  67. new MapPoint(120,-55),
  68. new MapPoint(110,-45),
  69. new MapPoint(110,-30)
  70. };
  71. ESRI.ArcGIS.Geometry.Polygon pg = new ESRI.ArcGIS.Geometry.Polygon();
  72. pg.Rings.Add(pc2);
  73. graphics[6].Geometry=pg;
  74. graphics[7] = new Graphic();
  75. //MediaElement的Name属性只能在xaml中定义(见帮助),所以决定了MediaElement不能完全在cs代码中定义
  76. BearVideo.Source = new Uri("http://serverapps.esri.com/media/bear.wmv", UriKind.RelativeOrAbsolute);
  77. BearVideo.IsHitTestVisible=false;
  78. BearVideo.IsMuted=true;
  79. BearVideo.AutoPlay=true;
  80. BearVideo.Opacity=0;
  81. ESRI.ArcGIS.Geometry.Polygon pg2 = new ESRI.ArcGIS.Geometry.Polygon();
  82. ESRI.ArcGIS.Geometry.PointCollection pc3 = new ESRI.ArcGIS.Geometry.PointCollection()
  83. {
  84. new MapPoint(10,-20),
  85. new MapPoint(32,7),
  86. new MapPoint(62,-35),
  87. new MapPoint(11,-36),
  88. new MapPoint(10,-20)
  89. };
  90. pg2.Rings.Add(pc3);
  91. graphics[7].Geometry=pg2;
  92. graphics[7].Symbol = new SimpleFillSymbol()
  93. {
  94. Fill = new VideoBrush()
  95. {
  96. SourceName = BearVideo.Name,
  97. Opacity = 0.6,
  98. Stretch = Stretch.UniformToFill
  99. }
  100. };
  101. foreach (Graphic g in graphics)
  102. {
  103. glayer.Graphics.Add(g);
  104. g.MouseLeftButtonDown+=new MouseButtonEventHandler(graphic_MouseLeftButtonDown);
  105. }
  106. }
  107. private void graphic_MouseLeftButtonDown(object o,MouseButtonEventArgs e)
  108. {
  109. Graphic g=o as Graphic;
  110. MessageBox.Show(string.Format("Geometry:{0}\nSymbol:{1}",g.Geometry.GetType().ToString(),g.Symbol.GetType().ToString()));
  111. }

复制代码

可 以看到,完全能够在一个Graphic上定义一些事件,来达到程序的目的。大家可以试着把上面的内容在xaml中改写一遍。看到这里肯定会产生一个疑问: 难道每个Geometry的定义都这么困难吗?其实SilverlightAPI已经给我们提供了ESRI.ArcGIS.Draw(继承自xaml中的 Canvas)类,它能非常方便的捕捉到用户的鼠标操作,从而获取各种Geometry来供程序使用。
        可以把Draw理解成一块画板,调用Draw的Active()方法,就可以开始在画板上面绘画,程序会自动记录鼠标画出的每个Geometry, 调用DeActive()方法,停止绘画。Active()有一个DrawMode参数,它决定了我们即将在这个画板上画出的内容类 型:Point,Polyline,Polygon等。在画的过程中我们可以看到地图上可以实时反映出我们绘画的内容,而这些则利用了Draw的预定义 Symbol:DefaultMarkerSymbol,DefaultLineSymbol,DefaultPolygonSymbol等。对应关系如 下:

每当完成一个图形的绘制,就会触发Draw.OnDrawComplete事件,利用事件参数就可以获得Geometry,之后可以创建一个 Graphic,设置一个Symbol(一般使用Draw的预定义Symbol),把画好的这个Graphic添加到一个GraphicsLayer中。
        点击这里,查看一个比较完整的Graphics的例子。
最后来看一下这个例子的部分代码:

  1. <Grid.Resources>
  2. <esriSymbols:SimpleMarkerSymbol x:Name="DefaultMarkerSymbol" Color="Red" Size="12" Style="Circle" />
  3. <esriSymbols:CartographicLineSymbol x:Name="DefaultLineSymbol" Color="Red" Width="4" />
  4. <esriSymbols:SimpleFillSymbol x:Name="DefaultFillSymbol" Fill="#33FF0000" BorderBrush="Red" BorderThickness="2" />
  5. <esriSymbols:SimpleFillSymbol x:Name="DefaultPolygonSymbol" Fill="#33FF0000" BorderBrush="Red" BorderThickness="2" />
  6. </Grid.Resources>
  7. <esriraw x:Name="Draw1"
  8. DefaultRectangleSymbol="{StaticResource DefaultFillSymbol}"
  9. DefaultMarkerSymbol="{StaticResource DefaultMarkerSymbol}"
  10. DefaultLineSymbol="{StaticResource DefaultLineSymbol}"
  11. DefaultPolygonSymbol="{StaticResource DefaultPolygonSymbol}"
  12. Loaded="Draw1_Loaded"
  13. OnDrawComplete="Draw1_OnDrawComplete" />
  14. <Canvas VerticalAlignment="Top" HorizontalAlignment="Left" Margin="20,20,0,0" Width="430" Height="110">
  15. <Rectangle RadiusX="10" RadiusY="10" Width="430" Height="110" Fill="#98000000" Stroke="#FF6495ED" />
  16. <Rectangle Fill="#FFFFFFFF" Stroke="DarkGray" RadiusX="5" RadiusY="5" Canvas.Left="10" Canvas.Top="10" Width="410" Height="90" />
  17. <StackPanel Orientation="Vertical" Canvas.Top="5" Canvas.Left="20">
  18. <esriWidgets:Toolbar x:Name="ToolBar1" MaxItemHeight="80" MaxItemWidth="80" Width="380" Height="80"
  19. ToolbarIndexChanged="ToolBar1_ToolbarIndexChanged"
  20. ToolbarItemClicked="ToolBar1_ToolbarItemClicked">
  21. <esriWidgets:Toolbar.Items>
  22. <esriWidgets:ToolbarItemCollection>
  23. <esriWidgets:ToolbarItem Text="添加点">
  24. <esriWidgets:ToolbarItem.Content>
  25. <Image Source="imgs/DrawPoint.png" Stretch="UniformToFill" Margin="5" />
  26. </esriWidgets:ToolbarItem.Content>
  27. </esriWidgets:ToolbarItem>
  28. <esriWidgets:ToolbarItem Text="添加折线">
  29. <esriWidgets:ToolbarItem.Content>
  30. <Image Source="imgs/DrawPolyline.png" Stretch="UniformToFill" Margin="5" />
  31. </esriWidgets:ToolbarItem.Content>
  32. </esriWidgets:ToolbarItem>
  33. <esriWidgets:ToolbarItem Text="添加多边形">
  34. <esriWidgets:ToolbarItem.Content>
  35. <Image Source="imgs/DrawPolygon.png" Stretch="UniformToFill" Margin="5" />
  36. </esriWidgets:ToolbarItem.Content>
  37. </esriWidgets:ToolbarItem>
  38. <esriWidgets:ToolbarItem Text="添加矩形">
  39. <esriWidgets:ToolbarItem.Content>
  40. <Image Source="imgs/DrawRectangle.png" Stretch="UniformToFill" Margin="5" />
  41. </esriWidgets:ToolbarItem.Content>
  42. </esriWidgets:ToolbarItem>
  43. <esriWidgets:ToolbarItem Text="添加曲线">
  44. <esriWidgets:ToolbarItem.Content>
  45. <Image Source="imgs/DrawFreehand.png" Stretch="UniformToFill" Margin="5" />
  46. </esriWidgets:ToolbarItem.Content>
  47. </esriWidgets:ToolbarItem>
  48. <esriWidgets:ToolbarItem Text="停止添加动作">
  49. <esriWidgets:ToolbarItem.Content>
  50. <Image Source="imgs/StopDraw.png" Stretch="UniformToFill" Margin="5" />
  51. </esriWidgets:ToolbarItem.Content>
  52. </esriWidgets:ToolbarItem>
  53. <esriWidgets:ToolbarItem Text="清空绘制的图形">
  54. <esriWidgets:ToolbarItem.Content>
  55. <Image Source="imgs/eraser.png" Stretch="UniformToFill" Margin="5" />
  56. </esriWidgets:ToolbarItem.Content>
  57. </esriWidgets:ToolbarItem>
  58. </esriWidgets:ToolbarItemCollection>
  59. </esriWidgets:Toolbar.Items>
  60. </esriWidgets:Toolbar>
  61. <TextBlock x:Name="StatusTextBlock" Text="" FontWeight="Bold" HorizontalAlignment="Center"/>
  62. </StackPanel>
  63. </Canvas>

复制代码

  1. private void Draw1_Loaded(object sender, RoutedEventArgs e)
  2. {
  3. Draw1.Map = Map1;
  4. }
  5. private void Draw1_OnDrawComplete(object sender, ESRI.ArcGIS.DrawEventArgs args)
  6. {
  7. ESRI.ArcGIS.GraphicsLayer graphicsLayer = Map1.Layers["GLayer2"] as ESRI.ArcGIS.GraphicsLayer;
  8. ESRI.ArcGIS.Graphic graphic = new ESRI.ArcGIS.Graphic()
  9. {
  10. Geometry = args.Geometry,
  11. Symbol = _activeSymbol,
  12. };
  13. graphicsLayer.Graphics.Add(graphic);
  14. }
  15. private void ToolBar1_ToolbarIndexChanged(object sender, ESRI.ArcGIS.Widgets.SelectedToolbarItemArgs e)
  16. {
  17. StatusTextBlock.Text = e.Item.Text;
  18. }
  19. private void ToolBar1_ToolbarItemClicked(object sender, ESRI.ArcGIS.Widgets.SelectedToolbarItemArgs e)
  20. {
  21. Draw1.Deactivate();
  22. switch (e.Index)
  23. {
  24. case 0: // Point
  25. Draw1.Activate(ESRI.ArcGIS.DrawMode.Point);
  26. _activeSymbol = strobeSymbol;
  27. break;
  28. case 1: // Polyline
  29. Draw1.Activate(ESRI.ArcGIS.DrawMode.Polyline);
  30. _activeSymbol = DefaultLineSymbol;
  31. break;
  32. case 2: // Polygon
  33. Draw1.Activate(ESRI.ArcGIS.DrawMode.Polygon);
  34. _activeSymbol = DefaultPolygonSymbol;
  35. break;
  36. case 3: // Rectangle
  37. Draw1.Activate(ESRI.ArcGIS.DrawMode.Rectangle);
  38. _activeSymbol = DefaultFillSymbol;
  39. break;
  40. case 4: // Freehand
  41. Draw1.Activate(ESRI.ArcGIS.DrawMode.Freehand);
  42. _activeSymbol = waveLineSymbol;
  43. break;
  44. case 5: // Stop Graphics
  45. break;
  46. case 6: // Clear Graphics
  47. ESRI.ArcGIS.GraphicsLayer graphicsLayer = Map1.Layers["GLayer2"] as ESRI.ArcGIS.GraphicsLayer;
  48. graphicsLayer.ClearGraphics();
  49. break;
  50. }
  51. }

复制代码

大家可以注意一下例子中添加的点符号和曲线符号。只要有足够的想象力,完全可以利用Silverlight定制出非常炫的符号效果来。
原文地址:http://bbs.esrichina-bj.cn/ESRI/thread-44892-1-1.html

ArcGIS API for Silverlight开发入门(5):任务外包——Tasks

通过上一节的学习,我们已经知道了如何与GraphicLayer交互,但毕竟GIS不是一个画板,所以这节来看一下如何通过Silverlight API完成GIS中的分析功能。
        GIS之所以是一个通用的工具,就是因为它具有各种各样分析和处理数据的能力。Silverlight API中提供了Task,使我们能够轻松完成常见的分析任务。
        先来考虑一下吃饺子的场景。要想吃饺子,我们需要先去买菜,买肉,回家后在厨房里洗菜,揉面, 拌馅,包饺子,煮饺子,吃饺子,之后别忘了洗碗;另一种情况就是去饭馆,告诉服务员我要吃3两茴香,3两韭菜的饺子,然后等着饺子端到你面前,开吃,走人。
        在ArcGISServer程序开发中,要完成GIS的分析功能其实和吃饺子是一样的。用ADF编程就像在家里吃饺子,除了架设服务器,所有的工作 基本上也都得我们自己在服务器端来完成,要处理的地方比较多;而用客户端API编程相当于去外面吃饺子,我们只要把任务交给相应的Task,之后接受结果 就行了,不用做饺子。唯一不同的就是在外面吃完饺子别忘了付钱,而用Task完成分析任务则是免费的。这点也体现在使用客户端API中的Task时,是由 ArcGISOnline提供给你的,不需要自己购买AGS软件。
        现在来看看Silverlight API目前给我们提供了那些Task功能:
Query:能够在已经发布的服务数据中,通过属性条件(可以属性字段中进行关系判断,字符查找等),图形条件(与输入的图形相交、包含、相离等),或者是两者的组合,查询出满足条件的数据并返回。相当于Engine中的SpatialFilter,当然也是QueryFilter。
Find:在地图数据的属性字段中查找包含有关键字参数的数据并返回。
Identity:对鼠标当前点击位置上的数据进行辨识并返回结果,可以对多个图层的数据进行辨识。
Address Locator:输入经纬度,返回地址结果(Geocoding);输入一个地方的地址,返回经纬度结果(Reverse Geocoding)。由于国内地图数据保密工作做的相当好,这个Task暂时用不到。
Geometry Service:可以对输入的地理数据进行如缓冲区,动态投影,面积/周长量算等几何操作。
Geoprocessing:能够完成复杂的GIS任务,类似ToolBox中的工具。
        抽象一下,可以看出,Query完全可以完成Identity和Find的工作,但后两者在特定场合下使用起来比Query要方便的 多;Geoprocessing完全可以替代Geometry Service,但是在利用REST API编写的程序中,要尽量使用GeometryService。
        再抽象一下,Silverlight API中的这几个Task和JavaScript/FlexAPI中的Task是大同小异的,因为其实它们都是AGS 9.3 REST API中暴露出来的操作资源(OperationResource)见下图:

后面的代码中实际上也是把输入参数封装起来提交到了REST API的特定Endpoint上。要理解好客户端API中的Task,建议熟读AGS的REST SDK。
        Task的用法基本上相同,都遵循这几个步骤:初始化Task,设置Task所需参数,提交任务,等待服务器完成任务后,处理返回的结果;进饭馆,想好你要吃什么饺子,告诉服务员,等饺子做好端上来,开始吃。好了,下面我们就通过一个实例(点击这里,查看实例),来学习一下Query和Geometry两个Task的用法。

首先选择工具条中的画线工具,在屏幕上画一条曲线,会根据曲线自动生成一个距离100公里的缓冲区显示在地图上,之后开始查询缓冲区图形经过的州 (相交),将结果显示在地图上。可以单击每个州查看详细信息。这里假设你已学习了前几节的内容,只讨论Task用法的部分。
1、利用所画的线生成缓冲区。画线利用的是Draw工具中的Freehand,在这个动作完成后会触发Draw的OnDrawCompleted事件,自然可以在这里开始进行缓冲区的工作,用的是Geometry Service里的Buffer。
        初始化Geometry Service。假设已经在Map1中添加了ID为glayerResult的GraphicsLayer,linesymbolred是提前设置好的CartographicLineSymbol:

  1. private void Draw1_OnDrawComplete(object sender, DrawEventArgs args)
  2. {
  3. Draw1.Deactivate();//Freehand动作失效
  4. //将Freehand画的曲线显示在地图上
  5. GraphicsLayer glayer = Map1.Layers["glayerResult"] as GraphicsLayer;
  6. Graphic g = new Graphic();
  7. g.Symbol = linesymbolred;
  8. g.Geometry = args.Geometry;
  9. glayer.Graphics.Add(g);
  10. //初始化Geometry Service
  11. GeometryService geometrytask = new GeometryService("http://sampleserver1.arcgisonline.com/ArcGIS/rest/services/Geometry/GeometryServer");
  12. }

复制代码

GeometryService 的初始化使用构造函数来完成的,里面接受一个URL,这个是Geometry Service的REST APIEndpoint。顺便说一下,不同于其他服务比如MapService,一个GISServer只能发布一个GeometryService,并 且它的名称必须是Geometry。
        当一个Task完成时会触发Completed事件,失败时也有Failed事件,对这两个事件进行监听:

  1. geometrytask.BufferCompleted += new EventHandler(geometrytask_BufferCompleted);
  2. geometrytask.Failed += new EventHandler(geometrytask_Failed);

复制代码

设置Buffer操作所需的参数:

  1. BufferParameters bufferparameters = new BufferParameters();
  2. bufferparameters.Unit = LinearUnit.Kilometer;
  3. //必须指定下面两个spatialreference,否则buffer结果集为空
  4. bufferparameters.BufferSpatialReference = new SpatialReference(3395);
  5. bufferparameters.OutSpatialReference = Map1.SpatialReference;
  6. bufferparameters.Distances.Add(100);
  7. bufferparameters.Features.Add(g);

复制代码

BufferParameters 是专门用于Buffer的参数;BufferSpatialReference是将要Buffer的图形重新投影到这个坐标系下(常常需要根据地图数据所 在地方的情况来设置这个参数),并设置Buffer距离的单位为公里,Buffer的输出一般与地图坐标系一致;Buffer参数有一个Features 属性,是List类型,里面的Graphic都将被Buffer。下来将Buffer的任务提交到服务器(可以看出为什么这些动作要叫Task):

  1. geometrytask.BufferAsync(bufferparameters);

复制代码

以 上代码都放在Draw1_OnDrawComplete函数中。任务提交到服务器后,由GeometryService接管,计算,完成后会立刻将结果返 回给我们,通知我们结果已经完成的方式就是前面绑定的Completed事件。接收到结果后,首先将缓冲区显示出来:

  1. private void geometrytask_BufferCompleted(object sender, GraphicsEventArgs args)
  2. {
  3. if (args.Results.Count>0)
  4. {
  5. GraphicsLayer glayer = Map1.Layers["glayerResult"] as GraphicsLayer;
  6. Graphic g = new Graphic();
  7. g.Symbol = fillsymbolBuffer;
  8. g.Geometry = args.Results[0].Geometry;
  9. glayer.Graphics.Add(g);
  10. }
  11. }

复制代码

如图:

2、利用生成缓冲区的缓冲区进行空间查询。要达到我们的目的,就还需要进行一个Query的Task,那么就可以在这里马不停蹄的开始Query的Task。步骤基本都是一样的,初始化,设置参数,提交结果,处理结果:

  1. private void geometrytask_BufferCompleted(object sender, GraphicsEventArgs args)
  2. {
  3. if (args.Results.Count>0)
  4. {
  5. GraphicsLayer glayer = Map1.Layers["glayerResult"] as GraphicsLayer;
  6. Graphic g = new Graphic();
  7. g.Symbol = fillsymbolBuffer;
  8. g.Geometry = args.Results[0].Geometry;
  9. glayer.Graphics.Add(g);
  10. //初始化QueryTask
  11. QueryTask querytask = new QueryTask("http://sampleserver1.arcgisonline.com/ArcGIS/rest/services/Demographics/ESRI_Census_USA/MapServer/5");
  12. //准备接收结果或者处理失败的通知
  13. querytask.ExecuteCompleted += new EventHandler(querytask_ExecuteCompleted);
  14. querytask.Failed += new EventHandler(querytask_Failed);
  15. //设置Query Task所需的参数
  16. Query query = new Query();
  17. query.OutFields.Add("*");//也顺便设置了query.ReturnGeometry=true;
  18. query.Geometry = g.Geometry;
  19. query.SpatialRelationship = SpatialRelationship.esriSpatialRelIntersects;
  20. //向服务器上的对应图层提交任务
  21. querytask.ExecuteAsync(query);
  22. Map1.Cursor = System.Windows.Input.Cursors.Wait;
  23. }
  24. }

复制代码

这 里的查询实在美国州的图层上进行的,详细信息将QueryTask构造函数里的那个参数输入浏览器查看;query.Geometry是设置需要进行空间 查询的图形,就是上面缓冲区的结果;OutFields是查询结果需要返回的字段,这里返回全部字段,如果返回全部字段,则强制设置了 ReturnGeometry为true,如果我们不需要处理结果的图形信息,则可以将这个参数设为false,以节省流量,显然这里不是;空间关系可以 参考API,与Engine中的完全一致。
        接下来处理QueryTask完成后的结果:

  1. private void querytask_ExecuteCompleted(object sender, QueryEventArgs args)
  2. {
  3. GraphicsLayer graphicslayer = Map1.Layers["glayerResult"] as GraphicsLayer;
  4. FeatureSet featureset = args.FeatureSet;
  5. if (featureset != null && featureset.Features.Count > 0)
  6. {
  7. graphicslayer.ClearGraphics();
  8. listboxResults.Items.Clear();
  9. foreach (Graphic graphic in featureset.Features)
  10. {
  11. graphic.Symbol = fillsymbolresult;
  12. graphicslayer.Graphics.Add(graphic);
  13. }
  14. }
  15. MyMapTip.GraphicsLayer = graphicslayer;
  16. Map1.Cursor = System.Windows.Input.Cursors.Arrow;
  17. }

复制代码

上 面处理空间查询的结果只是将图形显示了出来,那么对于单击某个州后,显示出其详细信息该怎么办呢?从图一可以看出,用到了Silverlight的 DataGrid控件,信息从哪里去呢?记得上面我们设置结果中返回的全部属性字段吗?它们存储在每个Graphic的Attributes属性中。要么 绑定到DataGrid里,要么一条条添加……你可能已经发现了这条语句MyMapTip.GraphicsLayer =graphicslayer;,还记得第三节的Widgets吗?那里我们落下了MapTip这个小家伙,现在派上用场了。除了在这里设置MapTip 的GraphicsLayer属性外,在xaml中有如下的定义:

  1. <esriWidgets:MapTip x:Name="MyMapTip" BorderBrush="#99000000"
  2. BorderThickness="1" Title="详细信息" VerticalOffset="10"
  3. HorizontalOffset="10" Background="#DDFFFFFF" />

复制代码

仅此而已。MapTip会自动找寻自己GraphicsLayer中的Graphic,当鼠标悬停在某个Grpahic上时,会自动读取它的Attributes属性并显示,小玩具又发挥了大作用。
        别忘了万一处理任务失败时的提示:

  1. private void geometrytask_Failed(object sender, TaskFailedEventArgs args)
  2. {
  3. MessageBox.Show("Buffer Error:" + args.Error);
  4. }
  5. private void querytask_Failed(object sender, TaskFailedEventArgs args)
  6. {
  7. MessageBox.Show("Query failed: " + args.Error);
  8. Map1.Cursor = System.Windows.Input.Cursors.Arrow;
  9. GraphicsLayer graphicslayer = Map1.Layers["glayerResult"] as GraphicsLayer;
  10. graphicslayer.ClearGraphics();
  11. }

复制代码

本节内容完毕。上面讲的相对简略,要理解各个Task和参数的用法,还是需要熟悉Silverlight API和 前面提到的REST API。另外,Geoprocessing Service实际上是最强大Task,如果有自己的GISServer,完全可以在上面发布自制的Model或者Python脚本,以完成各种GIS分 析任务,简单的在线编辑也是可能的。它的用法也万变不离其宗:初始化,设置参数,提交任务,处理结果。不同的是GeoprocessingService 有两种提交任务的方法:同步和异步。前者服务器端处理完任务后会立即将结果发送回客户端;后者将任务提交后会得到服务器端返回的一个JobID,即使任务 处理完成也不会立即返回,而是需要你拿这个JobID去询问服务器:完成了吗?完成了吗?完成了吗?如果完成,则可以取回相应的结果。
        前面说到,虽然去外面吃饺子很方便,但是毕竟那是人家做好的,对于老饕来说还需要自己的口感,自己下厨毕竟能控制整个过程的方方面面,哪怕你想做出 饺立方也都是有可能的。同样,ADF编程可以调用服务器端的ArcObjects,让你为所欲为,这点是客户端API无论如何也办不到的。
原文地址:http://bbs.esrichina-bj.cn/ESRI/thread-45302-1-1.html

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

在用SilverlightAPI开发的过程中,不论是从客户端提交到服务器端的数 据,还是从服务器端返回客户端的数据,都要表现在浏览器中,具体的来说是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,于是支持由 ArcGISServer 9.3版本发布的经过缓存的地图服务;又比如ArcGIS Server9.2版本发布的缓存地图服务不支持REST方式连接,如果要在93的客户端API中使用的话,就可以通过 TiledMapServiceLayer扩展一个比如ArcGISTiledMapServiceLayer92,来支持92Server发布的缓存地 图服务;

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

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

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

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

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

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

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

整个过程在xaml中就可以实现,只需要在Map的Layers中插入以下代码即可:

  1. <esri:ArcGISTiledMapServiceLayer ID="StreetMapLayer" Url="http://server.arcgisonline.com/ArcGIS/rest/services/ESRI_StreetMap_World_2D/MapServer"/>
  2. <esri:FeatureLayer ID="featurelayer"
  3. Url="http://sampleserver1.arcgisonline.com/ArcGIS/rest/services/Specialty/ESRI_StatesCitiesRivers_USA/MapServer/0"
  4. Where="POP1990 > 75000" ClusterFeatures="True" FlareBackground="#99FF0000" FlareForeground="White" MaximumFlareCount="9"
  5. FeatureSymbol="{StaticResource markersymbol}">
  6. <esri:FeatureLayer.OutFields>
  7. <sys:String>CITY_NAME</sys:String>
  8. <sys:String>POP1990</sys:String>
  9. </esri:FeatureLayer.OutFields>
  10. <esri:FeatureLayer.MapTip>
  11. <Grid Background="LightYellow">
  12. <StackPanel Margin="5">
  13. <TextBlock Text="{Binding Converter={StaticResource MyDictionaryConverter},
  14. ConverterParameter=CITY_NAME, Mode=OneWay}" FontWeight="Bold" />
  15. <StackPanel Orientation="Horizontal">
  16. <TextBlock Text="Population (1990): " />
  17. <TextBlock Text="{Binding Converter={StaticResource MyDictionaryConverter},
  18. ConverterParameter=POP1990, Mode=OneWay}" />
  19. </StackPanel>
  20. </StackPanel>
  21. <Border BorderBrush="Black" BorderThickness="1" />
  22. </Grid>
  23. </esri:FeatureLayer.MapTip>
  24. </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中的ThematicRendering例子并没有采用这两种Renderer,而是人为地为每个Graphic设置 了不同的Symbol。目前看来虽然这两个Renderer有点鸡肋,但毕竟是现在3种客户端API中提供的唯一现成的Renderer,可以猜想也许下 个版本的SilverlightAPI中会有更加成熟的专题图Renderer直接供我们使用;

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

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

原文地址:http://bbs.esrichina-bj.cn/ESRI/thread-45537-1-1.html

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

通过上一节学习,可以看出在Silverlight API中不仅可以轻松使用ArcGIS Server9.3发布的地图服务,也可以通过继承相应的图层,引入其他的数据源,比如ArcGIS Server9.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中做缓存时,有一个LoadTiling Scheme from Google Map吗?按照这个TilingScheme将一个地图服务做缓存,然后查看它的conf.xml和ServiceDirectory,便完全可以取得这 几个参数了。另外关于如何获取Google Map的缓存,网上已经有非常多方法,这里就不再讨论了。

代码如下:

  1. public class GoogleMap:TiledMapServiceLayer
  2. {
  3. public override void Initialize()
  4. {
  5. this.FullExtent = new
  6. ESRI.ArcGIS.Geometry.Envelope(-20037508.342787,-20037508.342787,20037508.342787,20037508.342787);//(-180,
  7. -85.0511287798066,180, 85.0511287798066)
  8. {
  9. SpatialReference = new ESRI.ArcGIS.Geometry.SpatialReference(102113);
  10. };
  11. this.SpatialReference = new ESRI.ArcGIS.Geometry.SpatialReference(102113);
  12. //this.InitialExtent = this.FullExtent;
  13. this.TileInfo = new TileInfo()
  14. {
  15. Height = 256,
  16. Width = 256,
  17. Origin = new ESRI.ArcGIS.Geometry.MapPoint(-20037508.342787,
  18. 20037508.342787)//Origin = new ESRI.ArcGIS.Geometry.MapPoint(-180, 90)
  19. {
  20. SpatialReference = new ESRI.ArcGIS.Geometry.SpatialReference(102113)
  21. },
  22. Lods = new Lod[20]
  23. };
  24. double resolution = 156543.033928;
  25. for (int i = 0; i < TileInfo.Lods.Length; i++)
  26. {
  27. TileInfo.Lods[i] = new Lod() { Resolution = resolution };
  28. resolution /= 2;
  29. }
  30. base.Initialize();
  31. }
  32. public override string GetTileUrl(int level, int row, int col)
  33. {
  34. //google maps map
  35. //string baseUrl = "http://mt0.google.com/mt/v=ap.92&hl=zh-CN&x=";
  36. //string url = baseUrl + col.ToString() + "&y=" + row.ToString() + "&z=" + level.ToString() + "&s=";
  37. //return url;
  38. google maps satallite
  39. string baseUrl = "http://khm2.google.com/kh/v=38&hl=zh-CN&x=";
  40. string url = baseUrl + col.ToString() + "&y=" + row.ToString() + "&z=" + level.ToString() + "&s=";
  41. return url;
  42. }
  43. }

复制代码

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

原文地址:http://bbs.esrichina-bj.cn/ESRI/thread-45582-1-1.html

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

  1. <esri:Map x:Name="Map1" Loaded="Map1_Loaded">
  2. <esri:Map.Layers>
  3. <esriVE:TileLayer ID="VELayer" LayerStyle="AerialWithLabels" ServerType="Staging"/>
  4. </esri:Map.Layers>
  5. </esri:Map>

复制代码

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

  1. <script language="C#" runat="Server">
  2. private string VEAccountID = "你的ID(注意只是AccountID)";
  3. private string VEAccountPassword="你的密码";
  4. protected void Page_Load(object sender,EventArgs e)
  5. {
  6. _08_virtual_earth.Web.VirtualEarthService.TokenService.CommonService
  7. commenservice = new
  8. _08_virtual_earth.Web.VirtualEarthService.TokenService.CommonService();
  9. commenservice.Credentials = new System.Net.NetworkCredential(VEAccountID, VEAccountPassword);
  10. _08_virtual_earth.Web.VirtualEarthService.TokenService.TokenSpecification
  11. tokenSpec=new
  12. _08_virtual_earth.Web.VirtualEarthService.TokenService.TokenSpecification();
  13. tokenSpec.TokenValidityDurationMinutes=480;
  14. if (HttpContext.Current!=null && !HttpContext.Current.Request.IsLocal)
  15. {
  16. tokenSpec.ClientIPAddress=HttpContext.Current.Request.UserHostAddress;
  17. }
  18. else
  19. {
  20. tokenSpec.ClientIPAddress="127.0.0.1";
  21. }
  22. string token = "";
  23. token = commenservice.GetClientToken(tokenSpec);
  24. Xaml1.InitParameters = string.Format("token={0}", token);
  25. }
  26. </script>

复制代码

其中Xaml1是Silverlight插件的ID:<asp:Silverlight ID="Xaml1" runat="server"...
2、Silverlight插件载入时读出这个token。在App.xaml.cs中:

  1. private void Application_Startup(object sender, StartupEventArgs e)
  2. {
  3. VEtoken = e.InitParams["token"];
  4. this.RootVisual = new Page();
  5. }

复制代码

3、最后在加载地图控件后,交付token:

  1. private void Map1_Loaded(object sender, RoutedEventArgs e)
  2. {
  3. foreach (Layer layer in Map1.Layers)
  4. if (layer is TileLayer)
  5. (layer as TileLayer).Token = (Application.Current as App).VEtoken;
  6. }

复制代码

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

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

原文地址:http://bbs.esrichina-bj.cn/ESRI/thread-45835-1-1.html

posted on 2013-07-20 14:58 NET未来之路 阅读(...) 评论(...) 编辑 收藏

转载于:https://www.cnblogs.com/lonelyxmas/p/3202426.html

ArcGIS API for Silverlight开发入门相关推荐

  1. ArcGIS API for Silverlight开发入门准备

    原文:ArcGIS API for Silverlight开发入门准备 微软的Silverlight提供了跨浏览器和跨平台开发环境,在Web中可用于创建和展现富互联网应用(RIA,Rich Inter ...

  2. ArcGIS API for Silverlight开发入门(2):一个基础地图实例

    这节在一个地图实例的基础上,来对Silverlight API中的一些基本概念做一个总体了解,顺便熟悉一下Silverlight的开发知识.         点击这里,直接看效果. 下载 (722.0 ...

  3. arcgis api for flex 开发入门(九)webservices 的使用

    arcgis api for flex 开发入门(九)webservices 的使用 flex 本身对webservices有着良好的支持,我们可以调用互联网上的各种 webservices来结合es ...

  4. arcgis api for flex 开发入门(二)map 的创建

    arcgis api for flex 开发入门(二)map 的创建 在flex 中创建一个esri 的map ,你只需要使用<esri:Map>标签就可以轻松完成. 在<esri: ...

  5. arcgis api for flex 开发入门(七)Geometry service 的使用

    arcgis api for flex 开发入门(七)Geometry service 的使用 Geometry service 顾名思义,就是提供针对几何层级的服务,比如说Project,   Si ...

  6. 13 ArcGIS API for JavaScript开发入门文档

    写在前面 这篇文章写在我用ArcGIS API for JavaScript(后面统称为"ArcGIS JS API")开发了两年项目后的某一天夜里.写这篇文章主要是两个目的吧,第 ...

  7. arcgis api for flex 开发入门(五)查询

    在gis中,针对要素的查询是一个最基本的操作,也是最常用的操作之一. 下面我们介绍如何使用arcgis api for flex 来查询我们需要的东西. 要在arcgis api for flex中进 ...

  8. ArcGIS API for Silverlight开发

    http://bbs.esrichina-bj.cn/ESRI/thread-45836-1-2.html Ethan_Shan little sister's birthday https://b ...

  9. ArcGis api配合vue开发入门系列(二)距离以及面积的测量

    正文 首先自定义个工具栏,包括测量距离与测量面积的工具以及地图漫游. 图标的话是用的iconfont.我是把这个工具单独写在一个组件里面,这个组件里面里面会用到一些操作地图的方法,我在map这个组件里 ...

最新文章

  1. Equalizing Two Strings 冒泡排序or树状数组
  2. Navicat中怎样将SQLServer的表复制到MySql中
  3. 详解HTTP与HTTPS
  4. 2018年10月Top 10 Python开源项目
  5. SAP云平台CloudFoundry环境试用帐号过期了怎么办
  6. UI设计师分析用户的常用方法
  7. 如何开具和交付给客户电子发票
  8. 蓝桥杯 基础练习 01字串(水题)
  9. mysql命令行导入dmp文件,oracle的备份文件(dmp)导入mysql数据库方法
  10. Salt Returners-作业结果数据返回器的使用与定制开发方法
  11. GIS应用技巧之去除遥感影像黑边
  12. 到底什么是超融合呢?
  13. AFX_VIRTUAL
  14. Adobe Illustrator Mac使用教程
  15. 货郎问题的实例 c语言编程,货郎担问题的C语言解法
  16. 【编程题】【Scratch二级】2022.03 魔法星空
  17. 我的世界服务器修改视野,我的世界默认视野是多少度
  18. 一文梳理人脸识别,看完全都懂了!
  19. js ol.ui.alternation
  20. check的3种用法

热门文章

  1. 关于BufferedReader的read()及readLine()
  2. PDF文件如何进行格式转换?
  3. 下载谷歌文件方法推荐
  4. 基于java的零食商城微信小程序
  5. 无线WLAN隧道转发模式下数据的封装以及转发过程
  6. Nginx直接访问redis,lua访问redis
  7. 图标设计类型和风格总结
  8. win10任务栏任务不显示缩略图
  9. 万字攻略,详解腾讯面试(二,BAT等大厂必问技术面试题
  10. 计算机市场最主流的cpu,什么cpu好 最受欢迎cpu推荐【详解】