块和分块服务

本章中,首先将介绍块和分块服务的基本概念和原理,然后介绍如何在MapGuide Studio中通过用户界面来配置图层使用分块服务和如何通过MapGuide分块服务API来创建和使用块。

1.1 

对于AJAX ViewerFusion ViewerMapGuide服务器都是将地图渲染为一张图像发送给客户端的,每次当用户在浏览器中缩放或平移地图的时候,通常都会要求MapGuide服务器重新发送地图到客户端。试想一下,如果每次都重新渲染地图,这将是一个不小的开销。那么,是否可以将已经渲染好的地图缓存下来,当客户端浏览地图的时候直接从缓存中取出那些已经渲染好的地图,从而降低MapGuide服务器的开销。这个方案听起来是可行的,而且在许多的商业软件都使用了这样的技术,MapGuide也采用了类似的技术。

那么,MapGuide是如何缓存已经渲染为图像的地图的呢?在介绍MapGuide的缓存技术之前,让我们先看看它必须解决的问题。

问题1在大部分情况下,客户端需要的只是当前视图中需要显示部分的地图,而不是整个地图,如何只把当前视图对应的地图图像返回给客户端?

问题2地图中图层不同的可见性设置会导致不同的地图渲染结果,如何缓存这些不同的地图图像?

问题3在不同的地图比例尺下,地图需要被渲染为不同分辨率的图像,以保证总能获得清晰的地图。地图比例尺是一个连续的值,所以不可能在所有的地图比例尺为地图创建缓存,那么又该如何解决这个问题呢?

1.1.1 

对于问题1MapGuide采用的办法是将整个地图分割为固定大小的图像块进行缓存,这样服务器只需要将客户端当前视图对应的图像块返回给客户端,而客户端可以立即显示返回的块,无需等待所有块全部返回才显示地图。

可以通过MapGuide服务器配置文件ServerConfig.iniTileServiceProperties部分的属性设置块的大小, DefaultTileSizeX属性用于设置块的宽度,DefaultTileSizeY属性用于设置块的高度,它们的单位都为像素,默认值都为300

1.1.2  基地图、基层和基层组

对于问题2MapGuide引入了基地图(Bae Map)、基层(Base Layer)和基层组(Base Layer Group)的概念来解决这个问题。与地图的概念类似,基地图也是由图层组组成,每个图层组由若干个图层组成,我们将基地图中的图层组和图层分别称为基层组和基层。但是,基地图与地图并不是平行的概念,基地图只是地图的组成部分,图6-1显示了地图定义的Schema,可以看到每个地图可以包含一个基地图BaseMapDefinition。与普通地图不同的是:

l  基地图只能由基层组组成,不能直接包含一个基层。所以,在图6-1中可以看到,BaseMapDefinition只包含了BaseMapLayerGroup

l  基层没有可见性设置,只有基层组具有可见性设置,基层的可见性由其所属的基层组决定。所以,在图6-1中可以看到,BaseMapLayerGroup包含Visible元素,而BaseMapLayer没有Visible元素。

 Map Definition Schema

 

6-1 地图定义的Schema

 

由于只有基层组具有可见性设置,所以基础图层组的所有图层是在一起渲染。对于不同的基层组,它们被渲染为不同的图像块,缓存在不同的目录下。这样,就可以解决问题2

假设基地图中包含了两个基层组,一个基层组为可见,一个为不可见,当使用Viewer浏览地图时,只需要将可见基层组缓存的块返回给客户端;如果两个基层组都可见,那么两个基层组缓存的块都会返回给客户端,如果高层的基层没有透明区域,底层的层会被高层的基层隐藏。

1.1.3  基地图比例尺

基地图具有一组预定义的比例尺,它们由图6-1中的FiniteDisplayScale元素所定义,MapGuide只将这些比例尺下的地图图像缓存下来。当Viewer发送请求来查看某个比例尺的地图时,MapGuide会在预定义的比例尺中查找最接近当前请求的比例尺,返回此比例尺下缓存的块,这样就解决了问题3

1.1.4  创建基地图

MapGuide Studio提供了如图6-2所示的用户界面用于创建基地图,使用步骤如下:

1) MapGuide Studio中打开要添加基地图的地图。

2) Base Layers For Smooth Navigation面板中Layers By Group and Drawing Order下面,点击Base Layers(0 Groups, 0 Layers)

3) 点击Create A New Group按钮,你创建任意多个基层组,但必须至少创建一个基层组,基层组之间不可以嵌套。

4) 添加图层到基层组,不要将已经在加入地图中的图层加入基层组。

5) Set Fixed Scales for Incremental Zooming面板的Total number of scales文本框中输入比例尺的数目,Studio会自动创建一系列平均间隔的比例尺,并将它们显示在Generated scale list列表框中。

6) 保存对当前地图的修改。

 UI to Create Base Map

 

6-2 创建基地图的用户界面

1.2  块的缓存位置

块在MapGuide服务器中的缓存路径由如下五部分组成:

l  文件基路径

l  基地图比例尺索引

l  基层组

l 

l 

1.2.1  文件基路径

如果是Library资源库,那么文件基路径等于地图资源ID的路径加下划线和地图资源ID的名称。假设地图的资源IDLibrary://Sample/Shanghai.MapDefinition,那么对应的文件基路径为“Sample_Shanghai”。如果是Session资源库,那么文件基路径等于Session ID加下划线、加地图资源ID的路径加下划线和地图资源ID的名称。假设地图资源IDSession:70ea89fe-0000-1000-8000-005056c00008_en//Sample/Shanghai.MapDefinition

那么对应的BasePath为“70ea89fe-0000-1000-8000-005056c00008_Sample_Shanghai”。

1.2.2  基地图比例尺索引

基地图比例尺索引部分等于英文字符“S”加当前基地图的比例尺索引,例如“S0”。 那么,如何确定基地图的比例尺索引呢?当Viewer发送请求来查看某个比例尺的地图时,MapGuide会遍历基地图定义中FiniteDisplayScale元素的值,查找与当前请求的比例尺最为接近的第nFiniteDisplayScale元素 (0开始计数)n这个值就是当前请求对应的基地图比例尺索引。

给定如下的地图定义,假设Viewer发送请求查看比例尺为12830的地图,我们可以看到当前请求的比例尺与第6FiniteDisplayScale元素的值2828.125最为接近,所以当前请求对应的基地图比例尺索引6

 

<?xml version="1.0" encoding="UTF-8"?>

<MapDefinition xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xsi:noNamespaceSchemaLocation="MapDefinition-1.0.0.xsd">

  <Name>New Map</Name>

  <CoordinateSystem>LOCAL_CS[&quot;Non-Earth (Meter)&quot;,LOCAL_DATUM[&quot;Local Datum&quot;,0],UNIT[&quot;Meter&quot;, 1],AXIS[&quot;X&quot;,EAST],AXIS[&quot;Y&quot;,NORTH]]</CoordinateSystem>

  <Extents>

    <MinX>1880999.75</MinX>

    <MaxX>1899000.25</MaxX>

    <MinY>458999.75</MinY>

    <MaxY>463000.25</MaxY>

  </Extents>

  <BackgroundColor>ffffffff</BackgroundColor>

  <Metadata>&lt;MapDescription&gt;Raster&lt;/MapDescription&gt;</Metadata>

  <BaseMapDefinition>

    <FiniteDisplayScale>0.690460205078125</FiniteDisplayScale>

    <FiniteDisplayScale>2.7618399999999999</FiniteDisplayScale>

    <FiniteDisplayScale>11.047359999999999</FiniteDisplayScale>

    <FiniteDisplayScale>44.189450000000001</FiniteDisplayScale>

    <FiniteDisplayScale>176.75781000000001</FiniteDisplayScale>

    <FiniteDisplayScale>707.03125</FiniteDisplayScale>

    <FiniteDisplayScale>2828.125</FiniteDisplayScale>

    <FiniteDisplayScale>11312.5</FiniteDisplayScale>

    <FiniteDisplayScale>45250</FiniteDisplayScale>

    <FiniteDisplayScale>181000</FiniteDisplayScale>

    <BaseMapLayerGroup>

      <Name>Raster</Name>

      <Visible>true</Visible>

      <ShowInLegend>true</ShowInLegend>

      <ExpandInLegend>true</ExpandInLegend>

      <LegendLabel></LegendLabel>

      <BaseMapLayer>

         <Name>g-07</Name>

         <ResourceId>Library://Layers/g-07.LayerDefinition</ResourceId>

         <Selectable>true</Selectable>

         <ShowInLegend>true</ShowInLegend>

         <LegendLabel>g-07</LegendLabel>

         <ExpandInLegend>true</ExpandInLegend>

 </BaseMapLayer>

    </BaseMapLayerGroup>

 </BaseMapDefinition>

</MapDefinition>

1.2.3  基层组

基层组部分等于基层组的名称。

1.2.4 

行部分等于英文字符“S”加块所在的行组号。那么,如何确定一个块所在的行组呢?这取决于MapGuide服务器配置文件ServerConfig.iniTileServiceProperties部分属性TileRowsPerFolder的值,将块的行号除以这个值然后取整,得到的值就是块所在的行组号。假设TileRowsPerFolder值为30,给定一个行号为50的块,那么它的行组号为1,行部分等于“R1”。

1.2.5 

列部分等于英文字符“C”加块所在的列组。那么,如何确定一个块所在的列组呢?这取决于MapGuide服务器配置文件ServerConfig.iniTileServiceProperties部分属TileColumnsPerFolder的值,将块的列号除以这个值然后取整,得到的值就是块所在的列组号。假设TileColumnsPerFolder值为30,给定一个列号为50的块,那么它的列组号为1,列部分等于“C1”。

1.3  计算块的坐标

给定一个块的行号tileColumn和列号tileRow,块左下角坐标(tileMinX, tileMaxX)和右上角坐标(tileMinY, tileMaxY)的计算公式如下:

tileMinX = mapMinX + tileColumn * tileWidthMCS;

tileMaxX = mapMinX + (tileColumn+1) * tileWidthMCS;

tileMinY = mapMaxY - (tileRow +1) * tileHeightMCS;

tileMaxY = mapMaxY - tileRow * tileHeightMCS;

其中,mapMinX为块对应地图左下角的X坐标值,mapMaxY为右上角的Y坐标值,tileWidthMCS为的每个块的宽度,tileHeightMCS为每个块的高度,他们使用的单位是块所对应地图空间参考系的单位。tileWidthMCStileHeightMCS的计算公式如下:

tileWidthMCS = tileWidth * metersPerPixel * scale / metersPerUnit;

tileHeightMCS = tileHeight * metersPerPixel * scale / metersPerUnit;

其中,scale为块所对应的比例尺,tileWidthtileHeight为块的像素宽度和高度;metersPerPixel 为像素与米的转换系数,MapGuide总是使用96 DPI,即96个像素等于一英寸;meterPerUnit为块所对应地图空间参考系单位与单位米的转换系数。

开源版MapGuide中的方法MgServerRenderingService::RenderTile(...)正好实现了计算一个块坐标值的逻辑,那么我们就以这个方法为例展示一下如何计算一个块的坐标值。

 

MgByteReader* MgServerRenderingService::RenderTile(MgMap* map,

                                                   CREFSTRING baseMapLayerGroupName,

                                                   INT32 tileColumn,

                                                   INT32 tileRow)

{

    ......

    // 获取地图的比例尺索引

    double scale = map->GetViewScale();

    INT32 scaleIndex = map->FindNearestFiniteDisplayScaleIndex(scale);

    ......

    // 按照layer group的名称获取layer group对象

    Ptr<MgLayerGroupCollection> layerGroups = map->GetLayerGroups();

    Ptr<MgLayerGroup> baseGroup = layerGroups->GetItem(baseMapLayerGroupName);

    ......

    // 根据比例尺索引获取对应比例尺的值

    scale = map->GetFiniteDisplayScaleAt(scaleIndex);

    // 确保当前地图使用了与Tile相同的DPI

    map->SetDisplayDpi(MgTileParameters::tileDPI);

    // 获取地图的范围,从而得到地图左上角和右下角的坐标值

    Ptr<MgEnvelope> mapExtent = map->GetMapExtent();

    Ptr<MgCoordinate> pt00 = mapExtent->GetLowerLeftCoordinate();

    Ptr<MgCoordinate> pt11 = mapExtent->GetUpperRightCoordinate();

    double mapMinX = rs_min(pt00->GetX(), pt11->GetX());

    double mapMaxX = rs_max(pt00->GetX(), pt11->GetX());

    double mapMinY = rs_min(pt00->GetY(), pt11->GetY());

    double mapMaxY = rs_max(pt00->GetY(), pt11->GetY());

// 获得Tile所对应地图空间参考系单位与单位米的转换系数

double metersPerUnit  = map->GetMetersPerUnit();

// 计算得到像素与单位米的转换系数

    double metersPerPixel = METERS_PER_INCH / MgTileParameters::tileDPI;

double tileWidthMCS   = (double)MgTileParameters::tileWidth  *

metersPerPixel * scale / metersPerUnit;

double tileHeightMCS  = (double)MgTileParameters::tileHeight *

metersPerPixel * scale / metersPerUnit;

// 计算得到Tile左下角和右上角的坐标

double tileMinX = mapMinX + (double)(tileColumn  ) * tileWidthMCS;

    double tileMaxX = mapMinX + (double)(tileColumn+1) * tileWidthMCS;

    double tileMinY = mapMaxY - (double)(tileRow   +1) * tileHeightMCS;

    double tileMaxY = mapMaxY - (double)(tileRow     ) * tileHeightMCS;

    ......

}

1.4  分块服务

分块服务是用来管理分块的一种服务,它提供了获取块和清除缓存块的功能。

1.4.1  设置分块服务

MapGuide服务器配置文件ServerConfig.iniTilingServiceProperties部分用于配置分块服务,下表列出了所有可用的设置。

 

属性名称

描述

RenderOnly

用于设定是否只是渲染Tile,而不将Tile缓存到服务器上,0代表假,1代表真。

TileCachePath

用于指定Tile图像缓存的根目录。

TileColumnsPerFolder

用于指定每个Tile列组文件夹中包含的列数。

TileRowsPerFolder

用于指定每个Tile行组文件夹中包含的行数。

DefaultTileSizeX

用于指定Tile的像素宽度。

DefaultTileSizeY

用于指定Tile的像素高度。

ImageFormat

用于指定生成的Tile的格式,它的值可以为PNGPNG8GIFJPG

1.4.2  获取块

如下两个方法GetTile(…)用于为获取块,mapDefinition用于指定一个地图定义的资源IDmap用于指定一个地图实例,baseMapLayerGroupName用于指定地图中基层组的名称,tileColumntileRow分别用于指定块所在的行组号和列组号,scaleIndex用于指定块的比例尺索引。

MgByteReader GetTile(MgResourceIdentifier mapDefinition,

String baseMapLayerGroupName,

int tileColumn, int tileRow, int scaleIndex);

MgByteReader GetTile(MgMap map,

String baseMapLayerGroupName,

int tileColumn, int tileRow, int scaleIndex);

 

Sequence Diagram of Getting Tile

 

6-3 获取块的时序图

 

MapGuide服务器端,获取块的时序图如图6-3所示。当调用GetTile(…)方法时,分块服务会查看指定的块是否已经存在,如果存在,那么直接返回缓存的块;如果不存在,首先调用SE_Renderer渲染指定的块,然后将渲染结果保存为图像缓存在相应的文件夹,最后返回缓存的块。由此,我们知道MapGuide服务器并不是提前为基层地图生成块,而是按需生成块。如果你不在乎缓存块所占用的硬盘空间,那么可以编写一个小程序为每个块调用一遍GetTile(…)方法,让分块服务MapGuide提前为基层地图生成所有的缓存块,从而能够让客户端能够更加平滑地浏览地图。

1.4.3  获取块的大小

方法GetDefaultTileSizeX()GetDefaultTileSizeY()用于返回块的宽度和高度,它们的单位都是像素。

int GetDefaultTileSizeX();

int GetDefaultTileSizeY();

1.4.4  清除缓存块

   方法ClearCache(…)用于清除属于指定基地图缓存在服务器上的所有块,你可以使用3-3所示的MapGuide Web AP测试工具来调用此方法清除缓存块,手工删除指定地图在服务器上的对应缓冲文件夹也可以达到相同的效果。

void ClearCache(MgMap map);

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值