• C++ 如何根据地理坐标范围获取瓦片地图并使用CImage库实现多张图片(瓦片地图)的快速合并


    作者:虚坏叔叔
    博客:https://xuhss.com

    早餐店不会开到晚上,想吃的人早就来了!😄

    C++ 如何获取瓦片地图并使用CImage库实现多张图片(瓦片地图)的快速合并

    一、C++ 如何获取瓦片数据

    C++ 如何获取瓦片数据,这里通过向谷歌地图发起http请求,获取多个地图保存到本地,部分关键源码如下所示

    int CGenerateJpgAccessImpl::FetchTiles(int zoomLevel, int x, int y, int width, int height, int nMaxThreads, bool yReversed, bool bNewBuild)
    {
    	int failedCount = 0;
    
    	CString strTilePath = _strFullPath + L"瓦片";
    	if (CFileToolkit::FileExist(strTilePath))
    		CFileToolkit::DeleteToRecycle(strTilePath);
    	CFileToolkit::CreateDirectory(strTilePath);
    
    	vector<StructUrl2Path> vctUrl2Path;
    	int nJpgI = 0;
    	int nJpgJ = 0;
    	if (yReversed)
    	{
    		// 下载图片
    		for (int row = y - height + 1; row <= y; row++)
    		{
    			nJpgI++;
    			nJpgJ = 0;
    			for (int col = x; col <= x + width - 1; col++)
    			{
    				CString strJpgBwUrl;
    				CString strWorkSpace = CPathConfig::GetWorksapce();
    				strJpgBwUrl.Format(_T("http://tiles.google.com.cn:8510/TileService/Tile?type=Mapbox&layerStyle=Satellite&x=%d&y=%d&z=%d"), col, row, zoomLevel);
    				CString strFullPath;
    				strFullPath.Format(L"%s\\%s#%s.jpg", strTilePath, CStringToolkit::IntToStr(nJpgI), CStringToolkit::IntToStr(nJpgJ));
    				StructUrl2Path sup;
    				sup.strPat = strFullPath;
    				sup.strUrl = strJpgBwUrl;
    				vctUrl2Path.push_back(sup);
    				nJpgJ++;
    			}
    		}
    	}
    	else
    	{
    		// 下载图片
    		for (int row = y; row >= y - height + 1; row--)
    		{
    			nJpgI++;
    			nJpgJ = 0;
    			for (int col = x; col <= x + width - 1; col++)
    			{
    				CString strJpgBwUrl;
    				CString strWorkSpace = CPathConfig::GetWorksapce();
    				strJpgBwUrl.Format(_T("http://tiles.google.com.cn:8510/TileService/Tile?type=Mapbox&layerStyle=Satellite&x=%d&y=%d&z=%d"), col, row, zoomLevel);
    				CString strFullPath;
    				strFullPath.Format(L"%s\\%s#%s.jpg", strTilePath, CStringToolkit::IntToStr(nJpgI), CStringToolkit::IntToStr(nJpgJ));
    				StructUrl2Path sup;
    				sup.strPat = strFullPath;
    				sup.strUrl = strJpgBwUrl;
    				vctUrl2Path.push_back(sup);
    				nJpgJ++;
    			}
    		}
    	}
    	
    	#pragma omp parallel for num_threads(2 * omp_get_num_procs() - 1)
    	for (int i = 0; i < (int)vctUrl2Path.size();i++)
    	{
    		StructUrl2Path sup = vctUrl2Path[i];
    		DownLoadTile(sup.strUrl, sup.strPat);
    	}
    
    	return failedCount;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    bool CGenerateJpgAccessImpl::DownLoadTile(CString strUrl, CString strSaveFile)
    {
    	std::string strUrlA = CW2A(strUrl);;
    
    	CURL *curl;
    	curl_global_init(CURL_GLOBAL_ALL);
    	const std::string url = strUrlA;
    	curl = curl_easy_init();
    	curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
    
    	std::string full_path = CW2A(strSaveFile);
    	FILE *fp;
    	if ((fp = fopen(/*"C:/00zbb/test.jfif"*/full_path.c_str(), "ab")) == NULL)
    	{
    		curl_easy_cleanup(curl);
    		return false;
    	}
    
    	curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_data);
    	curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp);
    	curl_easy_setopt(curl, CURLOPT_USERAGENT, "libcurl-agent/1.0");//这个要加不然文件传输不完全,字符串内容可自定义
    	double downloadFileLenth = 0;
    
    	if (CURLE_OK == curl_easy_perform(curl))
    	{
    		curl_easy_getinfo(curl, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &downloadFileLenth);
    	}
    	fclose(fp);
    	curl_easy_cleanup(curl);
    
    	return true;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    下载完成后 图片如下文件夹:

    在这里插入图片描述

    二、使用CImage库实现多张图片(瓦片地图)的快速合并

    对于多个瓦片数据,想要实现瓦片数据的合并,应该如何实现呢,这里假设已经下载到了瓦片数据,并且瓦片的格式如下图

    	int nPicWidth = 256; //瓦片宽
    	int nPicHeight = 256; //瓦片高
    	int nTotalPicSize = nPicWidth*nPicHeight;
    
    	CImage dst;
    	dst.Create(width, height, 24, 0);//创建一个dst对象;参数意义分别为dst宽;dst高;没有alpha通道;
    
    	HDC hDC = dst.GetDC();
    	// #pragma omp parallel for num_threads(2 * omp_get_num_procs() - 1)
    	for (int nCur = 0; nCur < (int)vctFile.size(); nCur++)
    	{
    		int nCurMapRow = nCur / nFirRowSize;
    		int nCurMapCol = nCur % nFirRowSize;
    
    		CString strFileName = vctFile[nCur];
    
    		CImage curImageData;
    		curImageData.Load(strFileName);
    		HDC hDestDC = curImageData.GetDC();
    		
    		int xDest = 0;
    		int yDest = 0;
    		int nDestWidth = 256;
    		int nDestHeight = 256;
    		int xSrc = 0;
    		int ySrc = 0;
    
    		if (nCurMapCol == 0)
    		{
    			xDest = 0;
    			nDestWidth = nPicWidth - leftTopPixelX;
    			xSrc = leftTopPixelX;
    		}
    		if (nCurMapCol == 1)
    		{
    			xDest = (nPicWidth - leftTopPixelX);
    		}
    		if (nCurMapCol >1)
    		{
    			xDest = (nPicWidth - leftTopPixelX) + (nCurMapCol - 1) * nPicWidth;
    		}
    
    		if (nCurMapRow == 0)
    		{
    			yDest = 0;
    			nDestHeight = nPicHeight - leftTopPixelY;
    			ySrc = leftTopPixelY;
    		}
    		if (nCurMapRow == 1)
    		{
    			yDest = (nPicHeight - leftTopPixelY);
    		}
    		if (nCurMapRow > 1)
    		{
    			yDest = (nPicHeight - leftTopPixelY) + (nCurMapRow - 1) * nPicHeight;
    		}
    
    
    		if (xDest + nDestWidth > width)
    		{
    			nDestWidth = width - xDest;
    		}
    		if (yDest + nDestHeight > height)
    		{
    			nDestHeight = height - yDest;
    		}
    
    		BOOL bResult = ::BitBlt(hDC, xDest, yDest, nDestWidth, nDestHeight, hDestDC,
    			xSrc, ySrc, SRCCOPY);
    
    		curImageData.ReleaseDC();
    	}
    	dst.ReleaseDC();
    
    	CString strSaveFilePath = _strFullPath + _strProjectBaseName + L"\\";
    	if (!CFileToolkit::DirectoryExist(strSaveFilePath))
    		CFileToolkit::CreateDirectory(strSaveFilePath);
    
    	CString strSaveFileName = strSaveFilePath + _strProjectBaseName + L".jpg";
    	dst.Save(strSaveFileName);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80

    只需要不到1秒的时间就能合并100多张图片:

    在这里插入图片描述

    三、完整的代码下载

    下面是计算完整的流程:根据多个地理坐标(经纬度),计算这些地理坐标的范围内。去谷歌地图上获取范围内的所有瓦片,并完成最终的瓦片合并

    	// 读取json获取所有的地理坐标(根据自己的途径获取)
    	vector<AcGePoint3d> vcP = GetAllCoords(strFileName);
    	if (vcP.empty())
    		return;
    
    	// 计算包围框
    	CalcBox(strFileName, vcP);
    
    	_nZoomLevel = CalcZoomLevel();
    
    	// 计算填充 方便调整经纬度和层级
    	CalcPad(basePad, minSideLength);
    
    	_nZoomLevel = CalcZoomLevel();
    
    	// 下载合并所有瓦片的流程方法
    	Merge();
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    提供代码下载,代码包含完整核心步骤和算法,不能够独立运行,下载请慎重

    下载地址

    四、总结

    • 本文主要介绍C++ 如何获取瓦片地图并使用CImage库实现多张图片(瓦片地图)的快速合并
    • 如果觉得文章对你有用处,记得 点赞 收藏 转发 一波哦~

    💬 往期优质文章分享

    🚀 优质教程分享 🚀

    • 🎄如果感觉文章看完了不过瘾,可以来我的其他 专栏 看一下哦~
    • 🎄比如以下几个专栏:Python实战微信订餐小程序、Python量化交易实战、C++ QT实战类项目 和 算法学习专栏
    • 🎄可以学习更多的关于C++/Python的相关内容哦!直接点击下面颜色字体就可以跳转啦!
    学习路线指引(点击解锁)知识定位人群定位
    🧡 Python实战微信订餐小程序 🧡进阶级本课程是python flask+微信小程序的完美结合,从项目搭建到腾讯云部署上线,打造一个全栈订餐系统。
    💛Python量化交易实战 💛入门级手把手带你打造一个易扩展、更安全、效率更高的量化交易系统
    ❤️ C++ QT结合FFmpeg实战开发视频播放器❤️难度偏高分享学习QT成品的视频播放器源码,需要有扎实的C++知识!
    💚 游戏爱好者九万人社区💚互助/吹水九万人游戏爱好者社区,聊天互助,白嫖奖品
    💙 Python零基础到入门 💙Python初学者针对没有经过系统学习的小伙伴,核心目的就是让我们能够快速学习Python的知识以达到入门

    🚀 资料白嫖,温馨提示 🚀

    关注下面卡片即刻获取更多编程知识,包括各种语言学习资料,上千套PPT模板和各种游戏源码素材等等资料。更多内容可自行查看哦!

    请添加图片描述

  • 相关阅读:
    汇编基础(3) --X86-64
    x64内核实验3-页机制的研究(1)
    1100*B. Maximum Rounding(贪心)
    《基于散列表的电话号码查找系统》数据结构 课程设计
    ubuntu 查看5000端口是否开放
    linux基础篇
    数组16—flat() :递归地将数组展平到指定的深度
    聚观早报 |小鹏P7i 550版上市;零一万物发布大模型
    C++ 类和对象(7) 对象数组
    二叉树--经典面试题2
  • 原文地址:https://blog.csdn.net/huangbangqing12/article/details/124965969