你的分享就是我们的动力 ---﹥

基于DeepZoom技术的Bing Maps客户端实现研究

时间:2012-07-02 13:47来源:www.chengxuyuans.com 点击:

目前基于Silverlight技术的Web GIS客户端实现,包括微软Bing Maps Silverlight Control,以及开源的Web GIS客户端组件DeepEarth 项目,核心都是采用Silverlight中的DeepZoom技术实现。可能您已经知道 DeepZoom技术以MultiScaleImage控件为核心,其内部有一个 MultiScaleTileSource类型的源属性,主要用于设置 MultiScaleImage控件所要呈现的数据源,可以通过学习Deep Zoom Composer快速上手 。

通过Deep Zoom Composer创建基于图片的Silverlight项目,可以生成一个基于MultiScaleImage控件的图片查看应用,完全实现了图片 的平铺,以及平滑、缩放以及拖拽等功能特性。 实际上基于Silverlight的Web GIS客户端实现也是通MultiScaleImage控件来实现,核心 就在于通过MultiScaleTileSource属性针对不同的Web GIS地图瓦片数据(Image Tiles)提供商为MultiScaleImage控件实现一个数据源。

对于微软的Bing Maps,则需要通过了解Bing Maps Tile System,然后针对其图片映射系统发布出的地图服务编写 MultiScaleImage控 件的数据源实现就行了。在实现前还需要借助HTTP嗅探器工具抓获到Bing Maps的Tiles路径,如下路径格式:

街道地图:http://r{0}.ortho.tiles.virtualearth.net/tiles/r{1}.png?g=203

卫星地图:http://h{0}.ortho.tiles.virtualearth.net/tiles/h{1}.jpeg?g=203

有了上面的Bing Maps的Tiles路径,接下来就可以基于这两个路径为MultiScaleImage控件编写数据源的实现(既 MultiScaleTileSource)了,如下代码块:

public abstract class BingMapsTileSource : MultiScaleTileSource
{
     protected BingMapsTileSource()
         : base(int.MaxValue, int.MaxValue, 0x100, 0x100, 0)
     { }
}

通过上面代码块的TileSource的编程模板可以看出,BingMapsTileSource继承与DeepZoom的核心类 MultiScaleTileSource,其加载图片 数据的算法由MultiScaleTileSource的实例方法GetTileLayers()完成。因此要想实现按照自己的前面获取到的Bing Maps的Tiles路径去 获取Bing Maps的地图数据,就应该重写该方法的实现,让其更改加载数据的方式。

/// <summary>
/// 图层Tile算法
/// </summary>
/// <param name="tileLevel">缩放级别</param>
/// <param name="tilePositionX">X坐标</param>
/// <param name="tilePositionY">Y坐标</param>
/// <param name="tileImageLayerSources">图层源集合</param>
protected override void GetTileLayers(int tileLevel, int tilePositionX, int tilePositionY, IList<object>  tileImageLayerSources)
{
     int zoom = tileLevel - 8;
     if (zoom > 0)
     {
         string QuadKey = TileXYToQuadKey(tilePositionX, tilePositionY, zoom);
         string veLink = string.Format(UriFormat,new object[] { QuadKey[QuadKey.Length - 1], QuadKey  });
         var veUri = new Uri(veLink);
         tileImageLayerSources.Add(veUri);
     }
}

上面代码段中实现了按照Bing Maps的Tiles路径格式去计算完整的地图数据地址,然后将其加入到图层源列表中,需要主要的是这里用 到了Bing Maps Tile System中的一个方法TileXYToQuadKey(),该放松实现将图层的X,Y坐标值转化为Bing Maps的Tile QuadKey参数值, 也就是Tile地址(http://h{0}.ortho.tiles.virtualearth.net/tiles/h{1}.jpeg?g=203)中的第二个参数值。则BingMapsTileSource类完 整代码如下:

namespace BingMapsClient.TileSources
{
     public abstract class BingMapsTileSource : MultiScaleTileSource
     {
         protected BingMapsTileSource()
             : base(int.MaxValue, int.MaxValue, 0x100, 0x100, 0)
         { }
         /// <summary>
         /// 地图的 Tile映射地址
         /// </summary>
         public abstract string UriFormat { get; }
         /// <summary>
         /// 转换X,Y坐标值为地图的QuadKey参数值
         /// </summary>
         /// <param name="tileX"></param>
         /// <param name="tileY"></param>
         /// <param name="levelOfDetail"></param>
         /// <returns></returns>
         private static string TileXYToQuadKey(int tileX, int tileY, int levelOfDetail)
         {
             var quadKey = new StringBuilder();
             for (int i = levelOfDetail; i > 0; i--)
             {
                 char digit = '0';
                 int mask = 1 << (i - 1);
                 if ((tileX & mask) != 0)
                 {
                     digit++;
                 }
                 if ((tileY & mask) != 0)
                 {
                     digit++;
                     digit++;
                 }
                 quadKey.Append(digit);
             }
             return quadKey.ToString();
         }
         /// <summary>
         /// 图层 Tile算法
         /// </summary>
         /// <param name="tileLevel">缩放级别</param>
         /// <param name="tilePositionX">X坐标</param>
         /// <param name="tilePositionY">Y坐标</param>
         /// <param name="tileImageLayerSources">图层源集合</param>
         protected override void GetTileLayers(int tileLevel, int tilePositionX, int tilePositionY,  IList<object> tileImageLayerSources)
         {
             int zoom = tileLevel - 8;
             if (zoom > 0)
             {
                 string QuadKey = TileXYToQuadKey(tilePositionX, tilePositionY, zoom);
                 string veLink = string.Format(UriFormat,new object[] { QuadKey[QuadKey.Length -  1], QuadKey });
                 var veUri = new Uri(veLink);
                 tileImageLayerSources.Add(veUri);
             }
         }
     }
}

以上只是定义了一个基于Bing Maps的Tiles地址的通用TileSource模型,需要改变的就是Tile地址,既然UrlFormat属性。如果需要分别 实现加载街道地图和卫星地图的TileSource,只需要制定相应的Tile地址就OK了。如下实现代码:

public class BingMapsRoadTileSource : BingMapsTileSource
{
     public override string UriFormat
     {
         get { return "http://r{0}.ortho.tiles.virtualearth.net/tiles/r{1}.png?g=203"; }
     }
}
public class BingMapsAerialTileSource : BingMapsTileSource
{
     public override string UriFormat
     {
         get { return "http://h{0}.ortho.tiles.virtualearth.net/tiles/h{1}.jpeg?g=203"; }
     }
}

接下来看看如果将上面的工作成果应用到Silverlight中,在XAML中实现如下布局,主要也就是使用一个MultiScaleImage控件。

<Grid x:Name="LayoutRoot" Background="White">
     <MultiScaleImage Height="600" x:Name="msi" Width="600"/>
</Grid>

     msi.Source = new BingMapsRoadTileSource();

现在就是操作MultiScaleImage控件了,所谓操作其实也就是动态的获取或设置MultiScaleImage控件的某些属性以及调用某些方法。以 下是MultiScaleImage控件比较常用的属性或方法:

Source:即DeepZoomComposer导出的文件(单个文件为Info.Bin,集合为 Items.Bin)

SubImages:子图片,如果DeepZoomComposer导出的为集合的话,其将集合中每个元素作为一个MultiScaleSubImage对象存储在这个集合 中。对子图片的操作就全靠它了。

UsingSprings:是否启用其默认动画(就是那中很飘逸的感觉,取消则比较生硬了)

Viewport: 视口位置(可以简单理解成眼睛所在位置,有过3D编程经验的比较容易理解)

ViewportWidth:视口宽度,视口越宽看到的东西越到(感觉上离图片越远,或图片缩小了)

ViewportOrigin:视窗内显示图像的左上角坐标位置。

AspectRatio:宽高比。

ElementToLogicPoint():从元素坐标(物理坐标)转换为逻辑坐标 (元素坐标则是我们平时所说的普通坐标,逻辑坐标则是指元素左上角 为0,0点,右下角为1,1点而言的相对坐标)

LogicToElementPoint():与上述相反。

ZoomAboutLogicPoint(double, double, double):按逻辑坐标缩放,第一个参数指定缩放增量,后两个参数指定缩放中心。

MotionFinished事件:动画结束(或者说当你操作图片或子图后其运动结束)

比如说现在需要处理鼠标移动的事件,需要考虑两种情况,如果鼠标是拖拽的需要将地图拖动在放下的时候进行重新定位,如果只是简 单的移动则不需要进行地图定位,这就需要通过获取视窗大小(ViewportWidth)以及当前所在视窗内的坐标点(Point)来计算出新的坐标点 ,然后进行重定位实现。

private Point currentPosition;
private Point dragOffset;
private bool mouseIsDragging;
this.MouseMove += delegate(object sender, MouseEventArgs e)
{
     if (mouseIsDragging)
     {
         var newOrigin = new Point();
         newOrigin.X = currentPosition.X -
                         (((e.GetPosition(msi).X - dragOffset.X) / msi.ActualWidth) *  msi.ViewportWidth);
         newOrigin.Y = currentPosition.Y -
                         (((e.GetPosition(msi).Y - dragOffset.Y) / msi.ActualHeight) *  msi.ViewportWidth);
         msi.ViewportOrigin = newOrigin;
     }
};

本篇暂且介绍到这里,通过本篇的介绍,基本上实现了一个基于微软Bing Maps的最简单的的客户端实现,包括动态切换图源、缩放地图 、拖拽地图等相关功能。当然这样是非常不完美的,比如还可以增加功能导航菜单、地图比例显示、当前鼠标所在地理位置坐标显示以及地 名搜索等等诸多功能实现。

本文配套源码

转载注明地址:http://www.chengxuyuans.com/slverlight/41562.html