Silverlight Map 技术点总结
关于Silverlight Map,很长一段时间都在学习,一边学习一边开发,总是会遇到这样、那样的问题,现在把这一年多时间里遇到的问题都整理出来,给自己积累,也希望给其他人提供帮助。
一、基于位图呈现
著名的如GoogleMap,Bing Map,等等,展现在我们眼前的其实都是一小块一小块的图片,像拼图一样,在浏览器缓存里可以找到这些图,好好加以利用,还是可以有一番作为的,要问有没有专业的工具能按需下载某个区域的GoogleMap各层级图像,可以肯定的说有,但不一定好找。当然,牛人都是不屑这些工具的,他们大可以自己写个,也很简单。这里提供一个现成的工具Google Map Buddy。
关于效果的技术实现,GooleMap,不用多说,那早已是Ajax的一个标杆。而在Bing Map,Visual Earth Map,借助了Silverlight的伟大创意,发明了诸如弹簧动画,DeepZoom,MultiScaleImage等等技术,使图像的加载过程看起来更加流畅,舒适。
那么,在Silverlight中要怎样才能使用自己的地图实现这样流畅的效果呢 ?
问题1. 流畅的图片显示过程。这一点,借助DeepZoom 软件导出数据后作为数据源交给MultiScaleImage控件实现。
问题2. DeepZoom需要一张高分辨率的地图,但分级切割后很难知晓并控制切割好的图片整体大小,只能在MultiScaleImage控件中调整,这个问题没有很好解决。
问题3. 使用MultiScaleImage 显示地图后能有流畅效果,可是怎样才能知道鼠标当前位置的相对地图坐标 ? 这是一个转换问题。(下面说明)
问题4. 怎样在地图上能任意添加背景透明的图层,即可以理解为Canvas层,Canvas层上可以使用ItemsControls 添加任意的模板项,这些项可以是一些用户定义的小图标(如油田、机场、公交站牌、等等),当然,这些图标的地理坐标都是从数据库中读出,怎样转换成屏幕坐标并做绑定? 这也是一个转换问题。(下面说明)
问题 5. 即便解决了绑定和坐标转换,地图都是需要能放大缩小平移的,MultiScaleImage都提供了这些功能,就是说地图是没问题的,关键在于你添加的Canvas层,要与MutiScaleImage动态绑定,注意,只需要Canvas绑定,因为Canvas内部的元素都是相对坐标,会自动随Canvas变换坐标,所以可以不用考虑。
解决这个问题的方法在于使用Silverlight提供的依赖属性功能。MultiScaleImage的主要依赖属性项有两个,ViewportOrigin,ViewportWidth ,这两个属性也是控制地图缩放的和可视位置的关键属性,将Canvas的ScaleTransform 值设置绑定到ViewportWidth上,当然中间需要有一个值转换器,就是实现了IValueConverter接口的转换类,用于控制缩放的两个值之间的单向转换。
问题 6. 从外部控制显示在控件中心的地图地理坐标。这也是一个坐标转换问题,大家看过GoogleEarth中是否带有输入经纬度或者地理名称,那个球就会飞到那个地方去呢,其实就是一个先缩小、再放大的过程,但是这个过程是需要一个类似反正切函数的(时间,值)变换过程。
幸好Silverlight 的开发工具Blend 中为我们自动生成这个曲线的动画效果。而关于中心坐标控制,则按照指定坐标与控件中心位置的偏移计算出ViewportOrgin的相对偏移量就行。
问题 7. 当一切效果显示都基本搞定时,我们就只需要数据填充了,所以接下来的问题就是数据传输,Silverlight 的数据传输不能直接使用像ADO.Net等直接数据库交互,而是只能通过Web服务或Http请求的方式获取数据,这也是客户端web应用的通用方法,如Flex,Javascript 等等,那么如果只是获取数据,一般则使用WCF或者最新的.Net RIA Service技术,RIA Service 做的比较傻瓜化,基本上不需要考虑太多的问题,只要知道怎么调用就可以,WCF要自己定义数据契约,以实现用户类的可序列化,但毕竟要比WebService功能全面的多。在Silverlight客户端添加WCF服务后,会自动在Refrence.cs文件中重新生成数据契约类,方便调用(需要使用Silverlight 2+的版本在项目中添加WCF-Enable Service项)。序列化过程中遇到的最常见的问题就是遇到某个类UnKnowServiceType ,在服务契约定义时加上[ServiceKnownType(typeof(用户类))]就行了。
问题 8. 使用Silverlight+WCF数据传输会遇到端口问题,典型的现象就是程序在Debug期间运行通畅,但发布到IIS上后运行就会出现错误,客户端不能获取到数据,原因就在于调试时的应用程序中服 务地址和Silverlight中保存的服务地址是相同的,而发布到IIS后,项目地址后面的端口号可能就会不一样,这时候Silverlight就找不到服务了。当然这里讨论的是非跨域访问。
解决这个问题的办法有两种
1) 修改IIS的端口,使之与调试时动态分配的端口相同
2) 修改Web项目属性,设置web选项中Servery一栏的端口设置为已经存在的IIS的端口号,调试的时候务必把IIS先关掉,否则又会自动分配。
7. 坐标问题探讨
在地图应用中,最重要的就是坐标转换问题,也是很费神的问题,所以我们忽略SRID,采用垂直坐标系,如果是自己的应用,自己的地图(这里不是指调用其他World Map来只显示自己那一小块区域,而是就是自己某个行业拍下的局部地图数据,可能是气象,地质勘测,地形地貌等等),垂直坐标系已经足够应付。如果经纬度跨度很大,应用需求对经纬度误差会造成很大影响,那就需要考虑球面投影,这时候我们也有解决的办法,CodePlex上有开源项目ProJNet,使用这个组件,可以很好的解决这个问题。SharpMap中也调用了这个库,SharpMap是基于矢量图形的后台绘制,前台位图呈现。我会在下一章中写到,这里暂不讨论。
垂直坐标转换很简单,准备一个数据类GeographicBoundBox ,包含左上角地理坐标(Ex_Longtitude,Ex_Latitude),经度跨度(LongtitudeSpread),纬度跨度(LatitudeSpread),
控件高度(ActualHeight)和宽度(ActualWidth)。
那么下面就是简单的比例转换运算了:(以东经北纬为例)
每像素经度 :XRate = LongtitudeSpread / ActualWidth
每像素纬度 :YRate = LatitudeSpread / ActualHeight
每经度的偏移像素:1 / XRate
每纬度的偏移像素:1 / YRate
地理坐标到屏幕坐标转换 :
System.Windows.Point GeographicToScreen(Coordinate c)
{
return new Point( ( c.X – Ex_Longtitude ) / XRate ,(Ex_Latitude – c.Y) / YRate);
}
屏幕坐标到地理坐标转换:
Coordinate ScreenToGeographic(System.Windows.Point p)
{
return new Coordinate((Ex_Lontitude + p.X * XRate),Ex_Latitude – p.Y * YRate);
}
要与Canvas结合起来,转换时需要利用Canvas的相对坐标属性 Canvas.TopLeft进行绑定。