地址:http://multitouch.codeplex.com/
用法:
效果:
ps:debug时可以在WP的Emulator上模仿多点触控的动作,如上图;这样自己就不用根据GestureType去写放大、缩小的动作了。
世界上最好的SUV,就是自己的两条腿
地址:http://multitouch.codeplex.com/
用法:
效果:
ps:debug时可以在WP的Emulator上模仿多点触控的动作,如上图;这样自己就不用根据GestureType去写放大、缩小的动作了。
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 |
1: public override string GetTileUrl(int level, int row, int col)
2: {
3: string baseUrl = @"{0}/export?dpi=96&transparent=true&format=png8&bbox={1}%2C{2}%2C{3}%2C{4}&bboxSR=102100&imageSR=102100&size=256%2C256&f=image";
4:
5: double cornerCoordinate = 20037508.3427892;
6: double originResolution = cornerCoordinate * 2 / 256;
7: double resolution = originResolution;
8: for (int i = 0; i < level; i++)
9: {
10: resolution /= 2;
11: }
12: double xmin, ymin, xmax, ymax;
13: //double resolution = 39135.7584820001;
14: xmin = -cornerCoordinate + resolution * 256 * col;
15: ymin = cornerCoordinate - resolution * 256 * (row + 1);
16: xmax = -cornerCoordinate + resolution * 256 * (col + 1);
17: ymax = cornerCoordinate - resolution * 256 * row;
18:
19: return string.Format(baseUrl, Url, xmin, ymin, xmax, ymax);
20: }
然后再来看下我们自定义图层实现ArcGISDynamicMapServiceLayer功能的可行性。
关于动态投影。如果想要叠加到WGS84坐标的底图上,只需要将bboxSR和imageSR改成4326即可。这样便支持了动态投影。
关于“切片”格式ImageFormat。只需修改上面baseUrl中的format参数。这样一来既不需要在服务器端提前切图,也能动态改变“切片”的格式。
关于服务加载速度。如果对于出图速度不满意,则可以在服务器端发布若干个相同的服务,轮询使用每个服务来出图,可以达到并行加速的目的。
关于DisableClientCache。默认情况下,与ArcGISTiledMapServiceLayer一样,这些“切片”会缓存在客户端,便于再次浏览。如果服务器端数据有变,那么就无法看到最新的变化情况,这是缓存服务的一个缺点。在自定义的图层类型中,我们可以在exportmap操作的Url最后再加一个时间戳参数,比如_ts=DateTime.Now.Ticks.ToString(),那么就达到动态地图服务每次都能看到最新结果的目的。
关于控制子图层的可见性。REST SDK的ExportMap操作中有layers参数可以控制。比如layers=show:2,4,7,则只会显示第3、5、8图层内容。
关于图层内容过滤。ArcGISDynamicMapServiceLayer有LayerDefinitions属性可以用SQL语句来筛选地图服务的输出内容,而REST中的ExportMap方法也提供了layerDefs供我们调用。比如{"0":"POP2000 > 1000000","5":"AREA > 100000"} ,只输出第一个图层中POP2000字段大于1000000的要素,第六个图层中AREA>100000的要素。
关于ArcGIS Server 10中的TimeExtent。ArcGISDynamicMapServiceLayer有一个TimeExtent属性用来显示一定时间范围内的数据,而ExportMap方法也给我们提供了Time参数来实现这个功能;并且还有layerTimeOptions参数来控制每个图层的时间段(偏移)。
关于服务的元数据。ArcGISDynamicMapServiceLayer和ArcGISTiledMapServiceLayer中,都有一些关于服务元数据的属性。比如Capabilities,CopyrightText,Description,InitialExtent,FullExtent等,这些在REST的MapService资源中都已经暴露了出来,因此我们可以通过发送请求的方式,在自定义图层初始化的时候顺便取回这些数据。
关于ArcGISTiledMapServiceLayer中的TileInfo。继承TiledMapServiceLayer类的第一个条件就是得知Tiling Scheme。自然TileInfo也是探囊取物。
细心的朋友已经看出来,其实和ArcGISDynamicMapServiceLayer、ArcGISTiledMapServiceLayer一样,我们这里自定的图层也是对ArcGIS Server REST SDK的一个封装,但在显示方式和显示速度上有所改进。输入的是动态地图服务,有缓存服务的效果,但没有真正做过缓存,所以姑且把它叫做DynamicTileMapServiceLayer。
一贯风格,要有真相。还是来看一个实际例子感受一下吧:http://newnaw.com/pub/sl/dynamictilemapservicelayer/
最后还要提一下这种图层的一个硬伤:面图层的重复标注。具体原因请参见:How do I avoid duplicate labels in my cache?解决办法有两种,1、采用Annotation代替Label;2、不要使用Label。
源程序下载:http://www.arcgis.com/home/item.html?id=5e32a79350b241f38032f9ca0321ccde
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与服务器端的交互完全可以接受;而且通常拥有这种数据量的用户硬件资源一般都是可充分扩展的,因此多发布几个服务来加速出图完全可行。
无图无真相,看看这种办法的效果。别忘了,在服务器端你发布的仅仅是普通的动态地图服务。
好了,这几种方法各有千秋,供各位朋友按需选择。
可能有些朋友还不知道如何在客户端程序中显示本地的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 iOS的版本是1.0final,而它本身是用iOS SDK 4.0.2版本编译的,所以要求开发环境的iOS SDK是3.2或4版本以上。于是你按照要求,下载了最新的iOS SDK 4.2(November 22,2010)和ArcGIS API for iOS 1.0并安装之后,在XCode中按照ArcIGS模板新建一个简单的View-based ArcGIS Mapping Application,但这是会发现出现Base SDK Missing的编译错误。
Google一下,这个问题很普遍,解决办法就是Project->Edit Project Settings->Build标签页中的将默认的Base SDK从3.2版本选择成现有的iOS SDK 4.2。可这样依旧不能解决问题。关键还有一个步骤,Project->Set Active Target,选择成你现在的工程。尽管你会看到,Project->Set Active Target中只有你现在工程的名称,并且已经打钩了,但你仍然要用鼠标点击一下。
之后编译,便会在模拟器中看到HelloMap程序了。
之前使用ssh账户和myentunnel作为上国际互联网的代理,但是这个代理本身是socks类型。而ie浏览器只支持http方式的代理,所以将127.0.0.1:7070设置到ie代理后是没有作用的。
而利用Privoxy这个工具,就可以将socks代理转成http代理。转换方法如下:
最后在ie代理中填入127.0.0.1:6060,就可以访问国际互联网了。此时,也可以很方便的让别人的机器使用你的6060端口来上国际互联网~
这样做是为什么呢?因为有些软件,比如windows live writer,只支持http类型的代理,因此Privoxy还是很给力的。对了,这篇日志就是用windows live writer写的。
Esri认证考试项目已经正式上线。12月27日开始,Esri全球合作伙伴可以申考,2011年1月17日软件用户可以申考。 证书很简约大方:
ArcGIS API for Silverlight自带Maptip,鼠标悬停触发;但对于Javascript API和Flex API中由单击事件触发的InfoWindow,许多朋友一直想要这种效果。
目前2.1版的api中给出了基于Toolkit的InfoWindow例子,如果觉得它不好用或者不满意,可以参考我这个基于ElementLayer实现的InfoWindow的例子。
有同志说一天看不到我,其实我的qq一直是隐身的。
一般来说,只要不出差,一周里我会5*8小时在线,我的qq一般也是5*8小时隐身。而又一般来说,我是不出差的。
只要你不在qq上问我技术问题,我都会立刻进行回复;如果你问技术问题,我会故意拖延若干分钟至若干小时不等再给你回复,这样就显的我比较忙一些,也许你下次就不会在这么问了。因为技术问题讲起来比较麻烦,只靠打字是说不清楚的,而我又很懒,所以很讨厌在qq上回答技术问题;再说对于技术我也只是略懂皮毛,如果因为我的懒和水平不够而产生了歧义,对于问题的解答也是没有任何帮助的。
不过如果你通过电话来问我技术问题,那你肯定能听到一个非常热情和尽可能全面的解答。
有图有真相,看看我过去几个礼拜和接下来的几个礼拜将要做的一些事情。
移步这里:http://help.arcgis.com/en/webapi/silverlight/2.1/help/index.html?Whats_New.htm
推荐大家采用最新版本,因为新版本不仅修复了以前版本的所有bug,还提供了新的功能。即使没有arcgis server 10,仍然可以是用新版本的api。