QT实现地图瓦片转换的一种算法实现(附代码)

一、场景描述:

  目前主流的地图瓦片规则都是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倍,那么log4 detailRatio就是两种瓦片规则下,详尽内容最相近的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);
	}
}

三、总结

关键点如下:

  1. 同一级别下瓦片详细程度比例的计算
  2. 确定源level后,计算瓦片的大小比值
  3. 根据瓦片大小比确定使用的源瓦片以及裁剪的瓦片大小
  • 2
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值