一、场景描述:
目前主流的地图瓦片规则都是2的n次幂乘2的n次幂(2x4,4x8,8x16....),但是有些软件厂商采用的瓦片规则除外(比如5x10,10x20,20x40),如何将不同规则的瓦片转换到自己平台上使用,是本篇文章的目的。
本篇文章以将(2x4,4x8,8x16)瓦片转换成(5x10,10x20,20x40)瓦片为例
二、实现思路
1.首先确定两种规则下瓦片level之间的对应关系,即目标平台的某level级别下的瓦片应该使用源平台的哪一层level进行转换(两平台表达内容详尽程度最相近的level),定义结构瓦片规则如下
struct Tile_Rule{
int topLevelRowCount; //最高level的行数
int topLevelColCount; //最高level的列数
int size; //瓦片像素
int topLevel; //最高级level
int btmLevel; //最低级level
};
两种瓦片同一级别下详尽程度可以用像素数来表示,那么同一级别下瓦片的详尽程度比例是
//dstRule为目标瓦片规则,srcRule为源瓦片规则
double detailRatio = (dstRule.size*dstRule.size*dstRule.topLevelRowCount*dstRule.topLevelColCount)/(srcRule.size*srcRule.size*srcRule.topLevelRowCount*srcRule.topLevelColCount);
同一瓦片规则下,下一级level像素数是上一级的4倍,那么就是两种瓦片规则下,详尽内容最相近的level只差(比如:目标瓦片的第二级对应源瓦片的第四级,但是他们的索引不一定相差2,因为toplevel不一定以索引0开始计数),在此定义为int mp_levelStep
int mp_levelStep = qRound(qLn(detailRatio)/qLn(4));
2.确定了使用瓦片的level,下一步确定具体使用的瓦片(比如:目标瓦片level 3,2行2列瓦片应该由源瓦片level 5下面的哪几张瓦片进行合成)。
基本思路:计算瓦片的实际地理范围之比(在这里使用边长的比,即行数的比值),根据边长比计算要使用的瓦片,多张瓦片合成一张大图片,将图片裁剪成目标瓦片的规格,将裁剪后的图片拉伸成目标瓦片的分辨率。下面给出实现,row,col,level为请求的目标瓦片的行列号和level,image为最终输出瓦片
void TileConvert::Convert(int row,int col,int level,QImage& image)
{
//应该使用的源数据level
int srcUseLevel = mp_SrcTileRule.topLevel+(level-mp_DstTileRule.topLevel)+mp_levelStep;
if(srcUseLevel<=mp_SrcTileRule.btmLevel)
{
//目标行列数
int dstRowCount = mp_DstTileRule.topLevelRowCount*qPow(2,(level-mp_DstTileRule.topLevel));
int dstColCount = mp_DstTileRule.topLevelColCount*qPow(2,(level-mp_DstTileRule.topLevel));
//使用的源level下的行列数
int srcRowCount = mp_SrcTileRule.topLevelRowCount*(qPow(2,mp_levelStep+level-mp_DstTileRule.topLevel));
int srcColCount = mp_SrcTileRule.topLevelColCount*(qPow(2,mp_levelStep+level-mp_DstTileRule.topLevel));
//瓦片大小系数
double sizeRatio = (double)srcRowCount/dstRowCount;
//需要读取的瓦片行列号范围
int startCol,startRow,endCol,endRow;
if((double)(sizeRatio*(row))-(int)(sizeRatio*(row))==0)
startRow = (int)(sizeRatio*(row))-1;
else
startRow = (int)(sizeRatio*(row));
if((double)(sizeRatio*(col))-(int)(sizeRatio*(col))==0)
startCol = (int)(sizeRatio*(col))-1;
else
startCol = (int)(sizeRatio*(col));
if((double)(sizeRatio*(row+1))-(int)(sizeRatio*(row+1))==0)
endRow = (int)(sizeRatio*(row+1))-1;
else
endRow = (int)(sizeRatio*(row+1));
if((double)(sizeRatio*(col+1))-(int)(sizeRatio*(col+1))==0)
endCol = (int)(sizeRatio*(col+1))-1;
else
endCol = (int)(sizeRatio*(col+1));
//联合瓦片
int height = (endRow-startRow+1)*mp_SrcTileRule.size;
int width= (endCol-startCol+1)*mp_SrcTileRule.size;
//读取瓦片
QImage unitTile(width,height,QImage::Format::Format_RGB32);
QPainter painter(&unitTile);
QStringList filePathList;
for(int i=startRow;i<=endRow;i++)
{
for(int j=startCol;j<=endCol;j++)
{
QString filePath = mp_SrcDataPath+QString::number(srcUseLevel)+"/"+QString::number(i)+"/"+
QString::number(srcUseLevel)+"_"+QString::number(i)+"_"+QString::number(j)+".jpg";
QImage image(filePath);
if(!image.isNull())
{
int pos_x = (j-startCol)*mp_SrcTileRule.size;
int pos_y = (i-startRow)*mp_SrcTileRule.size;
painter.drawImage(pos_x,pos_y,image);
}
}
}
int clip_PosY = (sizeRatio*row-startRow)*mp_SrcTileRule.size;
int clip_PosX = (sizeRatio*col-startCol)*mp_SrcTileRule.size;
QImage clipTile = unitTile.copy(clip_PosX,clip_PosY,mp_SrcTileRule.size*sizeRatio,mp_SrcTileRule.size*sizeRatio);
image = clipTile.scaled(mp_DstTileRule.size,mp_DstTileRule.size,Qt::IgnoreAspectRatio,Qt::SmoothTransformation);
}
}
三、总结
关键点如下: