Tag Archives: Silverlight

ArcGIS客户端API中另一种图层类型的探讨:DynamicTileMapServiceLayer

    ArcGIS客户端API(Javascript/Flex/Silverlight)中,我们最常打交道的是ArcGISDynamicMapServiceLayer和ArcGISTiledMapServiceLayer两个类,基本每个地图中都要用到。它们都可以直接将服务器端发布的地图服务(MapService)作为图层,加载到客户端程序中,分别对应了动态地图服务和缓存地图服务。这两种图层类型各有优缺点。     ArcGISDynamicMapServiceLayer(动态地图服务)通常用于实时显示经常变化的数据,支持控制单个图层可见性,可动态投影。但缺点是显示效果较差,整个服务出图较慢;ArcGISTiledMapServiceLayer可以直接加载服务器端的缓存地图服务,显示效果好,速度快,但它的缺点正是ArcGISDynamicMapServiceLayer的优点,即不支持动态投影,不能控制图层可见性,服务器端需要提前生成缓存等。     这里尝试自己来在客户端封装一个类,创建一种新的客户端图层类型。它能够综合以上两个图层的优点,而克服其各自的缺点。大致总结一下我们要达到的目的: ArcGISDynamicMap ServiceLayer ArcGISTiledMap ServiceLayer 自定义的客户端图层 实时获取最新数据 Y N Y 切片方式显示服务 N Y Y 需要提前生成缓存 Y N 客户端缓存切片加快显示速度 N Y Y 利用subdomain加速缓存加载 N Y Y 支持动态投影 Y N Y 动态指定图像输出格式 Y N Y 控制子图层可见性 Y N Y 利用LayerDefination过滤数据 Y N Y 利用TimeExtent显示时态数据 Y N Y 其它缓存服务特性 N Y Y 其它动态服务特性 Y N Y       由于这种图层类型三种客户端API(Javascript/Flex/Silverlight)均可使用,因此在这里我们就不讨论具体的实现代码,只说明一下实现思路。     首先来解决以缓存服务的方式来显示动态服务的问题。《ArcGIS客户端API中加载大量数据的几种解决办法(以Silverlight API为例)》一文中其实已经提到,主要是继承TiledMapServiceLayer,其中获取切片的GetUrl()方法,返回值是利用REST SDK中的ExportMap拼接的Url。这样,我们就并不需要提前切图,输入动态地图服务,从而达到缓存地图服务的显示效果。 … Continue reading ArcGIS客户端API中另一种图层类型的探讨:DynamicTileMapServiceLayer »

ArcGIS客户端API中加载大量数据的几种解决办法(以Silverlight API为例)

    REST风格的一切事物方兴未艾,ArcGIS Server的客户端API(Javascript/Flex/Silverlight API)也逐渐站上了GIS舞台的中央。虽然客户端API给我们带来了更快捷的开发体验,更丰富的展现效果,但有些(奇怪的)需求还不能直接解决。比如要求在客户端API程序中显示大量图形(上万个),乍看之下,受到平台本身的性能制约无法完成,但我们的思维和时间一样,只要挤一挤,总还是有的。本文就来讨论一下如何在客户端API程序中显示大数据量图形的问题。以Silverlight API为例。     要将几何对象显示在客户端程序中,一般我们首先想到的办法就是GraphicsLayer。将服务器端的要素通过查询或者其他方式创建成客户端的Graphic用以显示,从而进一步交互。于是有了下面几种方法。 1、Cluster     比较成熟和理智的做法是,利用Cluster效果来处理大量的Graphic。对于这种办法,ArcGIS Server中的Javascript API,Flex API和Silverlight API均能做到。下图是Silverlight API中300,000个点的展示效果。(忽略数据从服务器到客户端的传输时间)     Google Maps,Bing Maps等也都采取了这种做法。     但是,不论什么原因,就是不想采用Cluster的办法,而要看到大量密密麻麻的点时心里才觉得踏实该怎么办?对于这种需求,也有对应的解决方案。 2、直接在前台绘制Graphic     但这种办法直接受到平台性能的限制。以Flex和Silverlight为例,在不影响地图操作的情况下,视图范围内一次性能够承受的Graphic数量大约是4000-5000个。注意,相对于绘制Graphic对象的操作来说,Geometry节点的个数可以忽略,也就是说这几千个Graphic既可以是点,也可以是面。这时候你的地图看起来是这样的(4000个Graphic): 3、用ElementLayer(Silverlight API独有)来模拟GraphicsLayer      ElementLayer是Silverlight API中独有的一种图层类型,直接继承自ESRI.ArcGIS.Client.Layer,用来承载Silverlight本身的一些布局控件(Framework Element)。它的最主要的用途是使得Silverlight本身的控件能够随着地图的放大缩小而自动变化。利用它我们可以在一定程度上模拟GraphicsLayer。比如Silverlight API中的InfoWindow。     GraphicsLayer之所以只能承载一定数目的Graphic,就是因为Graphic对象本身封装的内容比较多。如果我们仅需要展示这些点,并只在上面做一些简单查询,那么可以完全用ElementLayer来替代之。直接的好处就是,可以在客户端显示更多的数据。比如一个点,我们可以用更基本的Ellipse来代替Graphic。将Graphic的Attributes存储在Ellipse.Tag中,依然可以用上面提到的InfoWindow做DataBinding。初步做了一下试验,如果用ElementLayer来模拟GraphicsLayer,在不影响地图操作的情况下,视图范围内一次性能承载的图形个数大约是20,000-25,000个。这时候你的地图看起来像是这样(20,000个Ellipse):     顺便提一下,如果想在ElementLayer中来模拟动态的地图符号,甚至是时态图层也是可以的。对于Graphic,你可以定义Symbol的ControlTemplate;对于ElementLayer,则可以直接将带有任何动画的对象直接加入其中。     上面提到的3种方法都是纯客户端的,尤其第三种办法,再没有更多工作量的情况下已经基本将客户端的性能用到了极致。但对于想看到密密麻麻点的人来说,五位数的数量级很有可能并不能满足要求。要想将展示的图形个数再提高一个或两个数量级,达到几十万甚至上百万,那么只有来借用服务器端的性能了。 4、ArcGISDynamicMapServiceLayer     直接将所要展示的数据在服务器端发布成一个MapService,在客户端通过ArcGISDynamicMapServiceLayer来加载。这样的话客户端需要展示的仅仅是一张图片,没有任何压力。功能上,如果想查询的话可以使用Identify/Find/Query Task来达到目的。下图加载了一个本机的ArcGISDynamicMapServiceLayer(AGS10,msd发布,1个实例,core i7 2.6GHz,4g内存,win7 x64),服务只有一个点图层,记录数大约650,000条:     服务器端生成这张图片的时间大约13s,进行一次IdentifyTask操作时间是0.2s。这里大部分时间都花在了服务器端对数据的绘制上,因此局域网传输图片等因素可以暂不考虑。服务器端的绘制速度受硬件和软件两个方面的影响。以ArcGIS Server 10已经无法改变,理论上只能通过提高硬件配置来达到加快出图速度的目的。     ps:采用ArcGISDynamicMapServiceLayer时可结合服务器端的点抽希办法来一起使用(抽稀方法1,抽稀方法2)。但这样就失去了密密麻麻数据带给我们的“成就感”,不在赘述。     这种办法中,客户端的压力已经完全没有了,功能上也能通过变通来实现,但展现上有两个缺点:1、数据过多的话绘制速度太慢(40w条数据大约是6.7s,12w条数据大约是2.3s,3w条数据大约是0.6s,);2、出图过程中该服务一片空白,且移动过程中也会出现“白边”的现象。如果硬件配置也无法提高,还有没有更好的办法呢?办法是有的。 5、继承TiledMapServiceLayer来达到“动态切图”的目的     在Google Fusion Tables的例子中,我们可以看到,所有的点数据都是以切片的方式展现,这样的好处是,即使出图速度较慢,用户也能看到切片一个一个的加载过程,不至于长时间一片空白;移动过程中也不会有白边现象;且浏览过的地方都会被客户端缓存,不需要再次向服务器请求,既减少了服务器压力,也加快了显示速度。但明显Google不可能为上传的每张表格都去做切片处理,ArcGIS Server中如何能达到这种效果?     首先想到的是服务切图时的“Cache On Demand”设置。但第一个用户等待的时间就是服务器完成该比例尺下切图的时间,显然太长;并且切图后如果数据有变化,那么看到的切图就和实际数据不一致了。这条路走不通。     通过继承TiledMapServiceLayer,可以利用GetUrl方法来控制如何获取每一个切片。此时如果我们以动态地图服务为资源,利用REST SDK的ExportMap操作来生成每一个切片,就达到了“动态切图”的目的。不仅如此,我们还可以在服务器端发布多个相同的服务,在GetUrl方法中轮询使用这些服务,便可以达到“并行”出图的目的。此时每个服务生成的图片都是256*256大小,所包含的记录数会少很多,速度会明显加快。本机发布3个服务时,同样比例尺下的650,000条记录,所有“切片”加载完成大约需要6s,考虑到缓存服务的加载效果,用户完全可以接受。这种方法相比ArcGISDynamicMapServiceLayer,大大提高了出图的速度和效果。     关于此方法的几点说明:如何利用比例尺、行/列号来计算一个切片的范围,可以参考《ArcGIS Server的切图原理深入》;默认情况下,浏览过的“切片”会缓存在客户端,再次浏览时服务器端就不用动态出图了。如果不想在客户端缓存“切片”,保证服务器端数据发生变化后,客户端也能随时更新,则可以在请求后面加上时间戳,达到DisableClientCache的功能;对于这种数量级的数据展示来说,一般是局域网应用,因此Identify/Find/Query与服务器端的交互完全可以接受;而且通常拥有这种数据量的用户硬件资源一般都是可充分扩展的,因此多发布几个服务来加速出图完全可行。     无图无真相,看看这种办法的效果。别忘了,在服务器端你发布的仅仅是普通的动态地图服务。     好了,这几种方法各有千秋,供各位朋友按需选择。

ArcGIS API for Silverlight加载本地Shapefile文件

      可能有些朋友还不知道如何在客户端程序中显示本地的shapefile,我在这里再介绍一下。       在线例子:http://newnaw.com/pub/sl/shapefilereader/(底图是WGS 84坐标系)       效果:       有些时候需要将本地的数据,比如shapefile,加载到客户端API程序中进行预览。思路不外乎一个,就是读取数据,然后将里面的要素显示在GraphicsLayer上面。但读取本地数据的方法有两种:上传到服务器端,用AO读取,然后将结果传回客户端处理;直接在客户端读取。       由于Esri早在1998年就公开了shapefile文件格式,因此直接写底层代码读取shapefile成为了可能,比较著名的有开源的sharpmap类库等。所以理论上可以在客户端打开本地的shapefile,解析后直接将要素转换成Graphic从而显示在GraphicsLayer之上。在09年Silverlight API刚出来的时候,前辈viswaug就已经做了这个工作。开源的项目在这里:http://esrislcontrib.codeplex.com/,上面的例子就用到了其中的shapefilereader类。里面的其他功能,比如GeoRSS和HeatMap Layer都已经被收录到了最新的SL API中。       而对于其它文件格式,比如CAD,如果有相应的类库,那么在本地直接加载也是可能的。

ArcGIS API for Silverlight中的InfoWindow

        ArcGIS API for Silverlight自带Maptip,鼠标悬停触发;但对于Javascript API和Flex API中由单击事件触发的InfoWindow,许多朋友一直想要这种效果。         目前2.1版的api中给出了基于Toolkit的InfoWindow例子,如果觉得它不好用或者不满意,可以参考我这个基于ElementLayer实现的InfoWindow的例子。

ArcGIS API for Silverlight 2.0 beta发布

rt,姗姗来迟~ 几大亮点:支持编辑功能;支持与time-enabled数据交互;必须使用silverlight 4&vs 2010&expression  blend 4进行开发;要使用完整功能必须有arcgis server 10支持;支持wkt。 具体见http://help.arcgis.com/en/webapi/silverlight/help/?Whats_New.htm what’s new: Silverlight 4 is now required to develop Silverlight applications.  Silverlight 3 is no longer supported. Visual Studio 2010 and Expression Blend 4 are required when building Silverlight 4 applications. You can edit feature layers associated with an ArcGIS 10 Server feature service. The Map and layers support display of time enabled data. The enhanced geometry … Continue reading ArcGIS API for Silverlight 2.0 beta发布 »

ESRI Silverlight Application Plus(chs)

演示地址:http://newnaw.com/pub/sl/esrislappplus 其中一些布局感谢”金属狂人”的建议 版本:ArcGIS API for Silverlight 1.2 说明:安装好ArcGIS API for Silverlight 1.2后,在vs2008中会出现两个模板程序:ESRI Standard Map Application和ESRI Showcase Map Application。其中前者与广为流传的Flex Viewer很像,但界面过于简陋,功能过于简单,如果想以它为基础构建快速应用的话还得费点功夫。在它的基础上,我将Code Gallery中的一些功能添加了进来,并将界面基本汉化,下面放出程序代码供大家学习交流之用。 功能:放大/缩小,前/后视图,放大镜,缩略图,书签,Identify,图层属性表,3个测量功能,地址定位。 截图: 下载地址:http://bbs.esrichina-bj.cn/ESRI/thread-64924-1-1.html ps:地址搜索用到了Google Map API,具体应用时请注意版权问题。 pps:这个程序仅是个人集成,属于民间作品。 ppps:这两天之内ArcGIS API for Silverlight 2.0 beta版本便会放出,但要充分利用新的功能,必须拥有ArcGIS Server 10才行。所以对于暂时升级不了AGS的朋友,这个东西应该还是凑合能用一段时间的。

Silverlight中的Busy Indicator

        busy indicator就是执行任务或加载资源期间告诉使用者耐心等待的一个动画,有条的,圈的等。最简单的办法就是显示一个gif动画,ajax应用普及后出现了大量busy indicator,本人就收集了好多gif图片。也可以来在线生成一个。         但silverlight中不支持gif格式的图片,该如何解决这个问题呢?列出以下几种办法供有需要的参考: 文字显示。利用DispatcherTimer类来处理,例子见这里; Silverlight Toolkit中的Busy Indicator控件。不过目前只能显示条状的,对于钟爱转圈的人来说没办法了; 利用Silverlight中的动画。原理就是截取转圈的gif图画中的一帧出来,然后用RotateTansform和DoubleAnimation做一个循环为forever的从0到360度的动画,这个效果可以以假乱真,推荐使用; 利用.net image tools控件,据说可以在silverlight中插入gif。没有测试; 听上去很搞笑的一个办法,就是”在silverlight中播放swf动画”。有老外说silverlight3原生支持播放swf文件,至今没找到实现办法,这个想法本身也比较bug;另外就是可以利用html绝对定位,在silverlight上盖一个swf动画,这到是可行的。但对于小小的busy indicator,需要把它放到比较精确的位置上,浏览器大小稍有改变可能就露出马脚了。

在Silverlight中为UIElement.Visibility添加动画

       Visibility可以控制UIElement的可见性,但其是一个枚举变量,msdn中说:“若要对作为枚举的值进行动画处理,必须使用 DiscreteObjectKeyFrame。”而关键帧动画没法做出平滑的过渡效果。        可以利用Action来实现标题内容。 1、创建一个带opacity动画的Action。 using System.Windows; using System.Windows.Interactivity; using System.Windows.Media.Animation; using System; namespace SilverlightApp.Actions { public class ToggleVisibilityAction : TargetedTriggerAction { protected override void Invoke(object parameter) { DoubleAnimation da = new DoubleAnimation(); da.Duration = new System.Windows.Duration(TimeSpan.FromSeconds(0.3)); Storyboard sb = new Storyboard(); sb.Children.Add(da); Storyboard.SetTarget(da, this.Target); Storyboard.SetTargetProperty(da, new PropertyPath(“Opacity”)); if (this.Target.Visibility==Visibility.Visible) { da.To = 0; sb.Begin(); sb.Completed += (o, e) => { this.Target.Visibility = Visibility.Collapsed; }; } … Continue reading 在Silverlight中为UIElement.Visibility添加动画 »

ArcGIS API for Silverlight中专题地图的实现浅析

        专题地图是突出表现特定主题或者属性的地图。常见专题地图类型有唯一值渲染,分类渲染,柱状图,饼状图,点密度图等。这些在ArcMap里,图层属性的Symbology标签中已有很好的诠释。         三种客户端API中目前为我们提供了现成的UniqueValueRenderer和ClassBreakRenderer功能,但有些食之无味,弃之可 惜。原因有二:1、领导常常并不认可这两种简约而不简单的专题图,因为他们没有看到复杂的圆饼和长柱;2、API中提供的两种专题图利用 GraphicsLayer自己实现也比较简单。这次以Silverlight API为例,探讨一下传统专题图实现的可能性。         要呈现专题图,有两个步骤:         1、绘制chart,比如饼图和柱图。有以下完成途径:Google Chart API,Silverlight Toolkit中的Chart功能(开源),Visifire(开源)。前者使用最简单,后者效果最好,暂时抛弃中庸的。         2、将绘制好的图形显示出来。因为是与地图服务无关的数据,所以可用GraphicsLayer来完成,而且可以利用其中的Cluster功能,推荐使用此办法。此时需要将chart定制为Graphic的Symbol;也可以利用ElementLayer来显示,此时需要将chart定制为UIElement。         通过三个例子来说明,专题要素是我国人口总数、城市人口数、非城市人口数。         1、选用GraphicsLayer,利用cluster功能,通过google api绘制静态chart。大概步骤:通过继承GraphicsCluster基类实现cluster功能,通过形如:http://chart.apis.google.com/chart?chs=100×100&chd=t:60,40&cht=p3的请求利用google chart tools绘制chart,通过PictureMarkerSymbol承载chart,交给Graphic显示。效果如图:         补充说明:1、通过google chart创建的静态图形没办法“说话”,不过可以利用GraphicsLayer的Maptip来实现简单交互;2、google chart目前还没办法创建背景透明的chart,Silverlight目前也不能去除位图背景色,但可以通过服务器上的GDI+功能来使背景透明,然后显示。         2、选用GraphicsLayer,利用cluster功能,通过visifire绘制可交互的chart。大概步骤:通过继承 GraphicsCluster基类实现cluster功能,通过继承MarkerSymbol来允许visifire产生的chart作为Symbol 赋给Graphic,最后显示Graphic。效果如图:         补充说明:visifire目前暂不支持databinding,所以利用改变MarkerSymbol的ControlTemplate办法无法动态修改chart数据。         3、选用ElementLayer,通过visifire绘制可交互的chart。大概步骤:根据专题要素动态创建可交互的chart,利用Graphic的Geometry来定位。效果如图:         补充说明:可根据比例尺处理chart的详细程度,比如小比例尺时尽量精简chart,避免互相压盖,大比例尺时显示详细的chart信息;必要时可自己实现cluster算法。