本章中,首先将介绍块和分块服务的基本概念和原理,然后介绍如何在MapGuide Studio中通过用户界面来配置图层使用分块服务和如何通过MapGuide分块服务API来创建和使用块。
对于AJAX Viewer和Fusion Viewer,MapGuide服务器都是将地图渲染为一张图像发送给客户端的,每次当用户在浏览器中缩放或平移地图的时候,通常都会要求MapGuide服务器重新发送地图到客户端。试想一下,如果每次都重新渲染地图,这将是一个不小的开销。那么,是否可以将已经渲染好的地图缓存下来,当客户端浏览地图的时候直接从缓存中取出那些已经渲染好的地图,从而降低MapGuide服务器的开销。这个方案听起来是可行的,而且在许多的商业软件都使用了这样的技术,MapGuide也采用了类似的技术。
那么,MapGuide是如何缓存已经渲染为图像的地图的呢?在介绍MapGuide的缓存技术之前,让我们先看看它必须解决的问题。
问题1:在大部分情况下,客户端需要的只是当前视图中需要显示部分的地图,而不是整个地图,如何只把当前视图对应的地图图像返回给客户端?
问题2:地图中图层不同的可见性设置会导致不同的地图渲染结果,如何缓存这些不同的地图图像?
问题3:在不同的地图比例尺下,地图需要被渲染为不同分辨率的图像,以保证总能获得清晰的地图。地图比例尺是一个连续的值,所以不可能在所有的地图比例尺为地图创建缓存,那么又该如何解决这个问题呢?
对于问题1,MapGuide采用的办法是将整个地图分割为固定大小的图像块进行缓存,这样服务器只需要将客户端当前视图对应的图像块返回给客户端,而客户端可以立即显示返回的块,无需等待所有块全部返回才显示地图。
可以通过MapGuide服务器配置文件ServerConfig.ini中TileServiceProperties部分的属性设置块的大小, DefaultTileSizeX属性用于设置块的宽度,DefaultTileSizeY属性用于设置块的高度,它们的单位都为像素,默认值都为300。
对于问题2,MapGuide引入了基地图(Bae Map)、基层(Base Layer)和基层组(Base Layer Group)的概念来解决这个问题。与地图的概念类似,基地图也是由图层组组成,每个图层组由若干个图层组成,我们将基地图中的图层组和图层分别称为基层组和基层。但是,基地图与地图并不是平行的概念,基地图只是地图的组成部分,图6-1显示了地图定义的Schema,可以看到每个地图可以包含一个基地图BaseMapDefinition。与普通地图不同的是:
l 基地图只能由基层组组成,不能直接包含一个基层。所以,在图6-1中可以看到,BaseMapDefinition只包含了BaseMapLayerGroup。
l 基层没有可见性设置,只有基层组具有可见性设置,基层的可见性由其所属的基层组决定。所以,在图6-1中可以看到,BaseMapLayerGroup包含Visible元素,而BaseMapLayer没有Visible元素。
图6-1 地图定义的Schema
由于只有基层组具有可见性设置,所以基础图层组的所有图层是在一起渲染。对于不同的基层组,它们被渲染为不同的图像块,缓存在不同的目录下。这样,就可以解决问题2。
假设基地图中包含了两个基层组,一个基层组为可见,一个为不可见,当使用Viewer浏览地图时,只需要将可见基层组缓存的块返回给客户端;如果两个基层组都可见,那么两个基层组缓存的块都会返回给客户端,如果高层的基层没有透明区域,底层的层会被高层的基层隐藏。
基地图具有一组预定义的比例尺,它们由图6-1中的FiniteDisplayScale元素所定义,MapGuide只将这些比例尺下的地图图像缓存下来。当Viewer发送请求来查看某个比例尺的地图时,MapGuide会在预定义的比例尺中查找最接近当前请求的比例尺,返回此比例尺下缓存的块,这样就解决了问题3。
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) 保存对当前地图的修改。
图6-2 创建基地图的用户界面
块在MapGuide服务器中的缓存路径由如下五部分组成:
l 文件基路径
l 基地图比例尺索引
l 基层组
l 行
l 列
如果是Library资源库,那么文件基路径等于地图资源ID的路径加下划线和地图资源ID的名称。假设地图的资源ID为Library://Sample/Shanghai.MapDefinition,那么对应的文件基路径为“Sample_Shanghai”。如果是Session资源库,那么文件基路径等于Session ID加下划线、加地图资源ID的路径加下划线和地图资源ID的名称。假设地图资源ID为Session:70ea89fe-0000-1000-8000-005056c00008_en//Sample/Shanghai.MapDefinition
那么对应的BasePath为“70ea89fe-0000-1000-8000-005056c00008_Sample_Shanghai”。
基地图比例尺索引部分等于英文字符“S”加当前基地图的比例尺索引,例如“S0”。 那么,如何确定基地图的比例尺索引呢?当Viewer发送请求来查看某个比例尺的地图时,MapGuide会遍历基地图定义中FiniteDisplayScale元素的值,查找与当前请求的比例尺最为接近的第n个FiniteDisplayScale元素 (从0开始计数),n这个值就是当前请求对应的基地图比例尺索引。
给定如下的地图定义,假设Viewer发送请求查看比例尺为1:2830的地图,我们可以看到当前请求的比例尺与第6个FiniteDisplayScale元素的值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["Non-Earth (Meter)",LOCAL_DATUM["Local Datum",0],UNIT["Meter", 1],AXIS["X",EAST],AXIS["Y",NORTH]]</CoordinateSystem>
<Extents>
<MinX>1880999.75</MinX>
<MaxX>1899000.25</MaxX>
<MinY>458999.75</MinY>
<MaxY>463000.25</MaxY>
</Extents>
<BackgroundColor>ffffffff</BackgroundColor>
<Metadata><MapDescription>Raster</MapDescription></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>
|
基层组部分等于基层组的名称。
行部分等于英文字符“S”加块所在的行组号。那么,如何确定一个块所在的行组呢?这取决于MapGuide服务器配置文件ServerConfig.ini中TileServiceProperties部分属性TileRowsPerFolder的值,将块的行号除以这个值然后取整,得到的值就是块所在的行组号。假设TileRowsPerFolder值为30,给定一个行号为50的块,那么它的行组号为1,行部分等于“R1”。
列部分等于英文字符“C”加块所在的列组。那么,如何确定一个块所在的列组呢?这取决于MapGuide服务器配置文件ServerConfig.ini中TileServiceProperties部分属性TileColumnsPerFolder的值,将块的列号除以这个值然后取整,得到的值就是块所在的列组号。假设TileColumnsPerFolder值为30,给定一个列号为50的块,那么它的列组号为1,列部分等于“C1”。
给定一个块的行号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为每个块的高度,他们使用的单位是块所对应地图空间参考系的单位。tileWidthMCS和tileHeightMCS的计算公式如下:
tileWidthMCS = tileWidth * metersPerPixel * scale / metersPerUnit;
tileHeightMCS = tileHeight * metersPerPixel * scale / metersPerUnit;
其中,scale为块所对应的比例尺,tileWidth和tileHeight为块的像素宽度和高度;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;
......
}
|
分块服务是用来管理分块的一种服务,它提供了获取块和清除缓存块的功能。
MapGuide服务器配置文件ServerConfig.ini中TilingServiceProperties部分用于配置分块服务,下表列出了所有可用的设置。
属性名称
|
描述
|
RenderOnly
|
用于设定是否只是渲染Tile,而不将Tile缓存到服务器上,0代表假,1代表真。
|
TileCachePath
|
用于指定Tile图像缓存的根目录。
|
TileColumnsPerFolder
|
用于指定每个Tile列组文件夹中包含的列数。
|
TileRowsPerFolder
|
用于指定每个Tile行组文件夹中包含的行数。
|
DefaultTileSizeX
|
用于指定Tile的像素宽度。
|
DefaultTileSizeY
|
用于指定Tile的像素高度。
|
ImageFormat
|
用于指定生成的Tile的格式,它的值可以为PNG、PNG8、GIF或JPG。
|
如下两个方法GetTile(…)用于为获取块,mapDefinition用于指定一个地图定义的资源ID,map用于指定一个地图实例,baseMapLayerGroupName用于指定地图中基层组的名称,tileColumn和tileRow分别用于指定块所在的行组号和列组号,scaleIndex用于指定块的比例尺索引。
MgByteReader GetTile(MgResourceIdentifier mapDefinition,
String baseMapLayerGroupName,
int tileColumn, int tileRow, int scaleIndex);
MgByteReader GetTile(MgMap map,
String baseMapLayerGroupName,
int tileColumn, int tileRow, int scale
分享到:
相关推荐
这可能涉及到排序块(根据块编号)和拼接块数据。 5. **错误处理**:在上传过程中,应处理各种可能出现的错误,如网络中断、超时等。可以设计重试机制,对失败的块进行重新上传。 四、相关技术 - **Web API**:在...
在分块上传的过程中,程序首先会将大文件分割成若干个小块(通常根据网络状况和服务器限制来确定块大小)。然后,对于每个块,程序会创建一个libcurl会话,设置FTP服务器的相关信息(如服务器地址、端口、用户名和...
UDS(统一诊断服务)协议是汽车行业的标准通信协议,用于ECU的诊断、编程和数据交换。在使用UDS协议进行ECU刷写时,往往需要对S19文件进行预处理,包括分块和校验码的计算。本文将深入探讨基于Labview实现的S19文件...
此外,在地图服务中,GDAL的分块读写功能也用于构建金字塔层级的图像,以支持不同比例尺下的高效数据渲染。 ### 总结 GDAL的读写图像分块处理技术是GIS和遥感领域的重要组成部分,它不仅解决了大数据量图像的处理...
在现代网络应用中,分块传输是一种常见且重要的数据传输技术,尤其是在处理大文件或在网络状况不佳的情况下,它可以提高传输效率和稳定性。 ### 分块传输的概念 分块传输是指将一个大的数据包分割成多个较小的数据...
分块传输编码的工作原理是,每个数据块都由一个表示块大小的十六进制数字开头,后跟块的实际内容,最后以两个连续的换行符(CRLF,即`\r\n`)结束。例如,一个大小为2个字节的块会被表示为`2\r\n`,紧接着是数据`ab`...
1. **分块策略**:代码中应包含一个算法来决定如何将大文件分割为多个块,通常块的大小是固定的,例如4MB或16MB。这有助于在服务器端进行重组。 2. **断点续传**:如果上传过程中网络出现问题,程序应该能够识别...
"批量上传图片"、"拖动上传"以及"分块上传"是提高用户体验和优化服务器性能的关键技术。以下是对这些概念的详细解释: 1. 批量上传图片:批量上传允许用户一次性选择多个文件进行上传,而不需要逐个点击上传。这极...
在Java开发中,大文件上传是一项常见的需求,尤其是在云存储、文件分享等场景下。由于网络环境的不稳定性,...同时,可以根据实际需求进行修改和扩展,比如增加并发上传的策略,优化用户体验,或者适配不同的存储服务。
当文件过大,一次性上传可能会导致内存溢出或者超时,因此采用分块上传的方式,将大文件分割成多个小块,每次上传一个块,直到所有块都上传完成。这种方式可以有效避免单次上传的压力,提高上传的成功率。 在...
其中,断点续传和分块下载技术是提升用户体验的关键。本文将详细探讨如何使用C#语言实现一个异步多线程的HTTP文件分块断点续传下载工具,并基于.NET Framework 2.0及以上版本进行开发。 首先,理解断点续传技术。...
这款软件提供了两种不同的工作模式,以满足用户在不同场景下的需求:分块模式和流畅的一体模式。 首先,我们来详细了解一下“分块模式”。这种模式通常适用于大文件或大量文件的转换。在分块模式下,PDF转换器会将...
在这个项目中,文件被分割成多个小块,每个块独立上传,这样即使某个块上传失败,只需重传该块即可,大大提高了上传的可靠性和效率。 3. **前端Vue.js** Vue.js是一个轻量级的前端MVVM(Model-View-ViewModel)...
[WebMethod(Description = "为了支持多点分块异步上传文件,此方法必须由客户端预先调用,以便在服务器端生成指定 FileName 和 Length 大小的空白文件预定空间!建议客户端同步调用")] public string ...
分块读取Blob的原理是将大文件分成较小的数据块,每次只从数据库中读取一块,而不是一次性加载整个Blob。在Delphi中,可以使用Oracle的ODAC(Oracle Data Access Components)库来实现。下面是一个简单的分块读取...
- **发送请求**:使用Ajax异步发送每个块,携带块的编号和总块数信息,以便服务器识别和重组。 - **后端处理**:在Asp.net的服务器端,创建一个临时文件夹,用于保存接收到的块。每个块到达时,将其写入对应编号的...
基于以上文件,我们可以推断,这个项目可能包含了一个使用CXF实现的Web服务,服务端包含处理分块文件上传的逻辑,而`wsdl`和`wsdl-src`文件可能描述了服务的接口。客户端可能通过调用这些服务接口,将大文件分割并逐...
10. **存储策略**:根据项目需求,你可能需要考虑如何存储这些临时的文件块,是直接保存到硬盘,还是使用云存储服务如Azure Blob Storage。 通过掌握以上知识点,你就可以构建一个功能完善的ASP.NET分块上传系统。...
研究基于云计算的数据安全,不仅对于防止数据泄露和未经授权的访问至关重要,而且对于维持云计算服务的商业信誉和用户的信任也具有重要的意义。 分布式AES加密算法的提出背景 云计算网络环境下数据传输与存储的特殊...
C#是一种广泛用于开发Windows桌面应用、Web应用和服务的编程语言,具有丰富的类库和强大的.NET框架支持。在这个项目中,开发者使用C#的Windows Forms库创建了一个图形用户界面,让用户可以直观地进行分块上传操作。 ...