获取到经纬度范围后,我们需要计算出瓦片的范围。
本文涉及的地图瓦片都以左上角为原点开始编号的,从左至右为 x 轴, 从上到下为 y轴。

为保证地图是方形,基于 Web 墨卡托投影的地图左上角经纬度坐标为(180°,85.0511 °),右下角经纬度为(-180°,-85.0511°)。纬度范围是[-85.0511, 85.0511 ]。
假设z为需要拼接的图层的层数,设n=2的z次方,lon为经度,lat为维度,则经纬度、层级和瓦片的坐标x、y的关系为:
TileX =(lon+180)÷360×n;
TileY = (1-(log(tan(lat))+sec(lat)))÷2×n;
在C#中,Math函数的三角参数要求为弧度,所以纬度值牵扯到三角函数的还需要×PI÷180。两个函数如下:

 /// <summary>/// 计算瓦片所在X值/// </summary>/// <param name="lng"></param>/// <param name="zoom"></param>/// <returns></returns>public static double getTileX(double lng, int zoom){double tileX = (lng + 180.0) / 360 * Math.Pow(2, zoom);return tileX;}/// <summary>/// 墨卡托投影下,通过维度计算瓦片所在Y值/// </summary>/// <param name="lat"></param>/// <param name="zoom"></param>/// <returns></returns>public static double getTileY(double lat, int zoom){if (lat > 90)lat = lat - 180;if (lat < -90)lat = lat + 180;double tileY = Math.Pow(2, zoom) / 2 * (1 - (Math.Log(Math.Tan(Math.PI * lat / 180) + 1 / Math.Cos(Math.PI * lat / 180))) / Math.PI);return tileY;}

前面将下载图片的函数已在第三章中写好,直接通过xyz和瓦片地址下载图片,然后使用GDI+拼接即可。
这里有一个很严重的问题,就是受GDI+技术限制,图幅过大可能拼接失败。一般10000像素×10000像素以内为妥。所以,过大范围的需求,只能拼接为系统承受范围内的大小了。我们做一个参数,可以设置这个值,最大到10240像素。用户最终在ps里再量力而行的拼接了。
写一个类DownloadWork,用于在线程里展开下载任务,在构造函数中声明下载范围和下载图片的参数。
GetArea(WorkArg arg)用于对任务进行拆解,以防止GDI+内存溢出报错:

        /// <summary>/// 以maxnum×maxnum为一组数据/// </summary>/// <param name="Arg"></param>/// <returns></returns>private List<AreaConfig> GetAreas(WorkArg Arg){List<AreaConfig> results = new List<AreaConfig>();for (int j = Arg.Starty, n = 0; j <= Arg.Endy; n++){for (int i = Arg.Startx, m = 0; i <= Arg.Endx; m++){AreaConfig area = new AreaConfig();area.Startx = i;area.Endx = i + GlobalConfig.maxtilecount < Arg.Endx ? i + GlobalConfig.maxtilecount : Arg.Endx + 1;area.Starty = j;area.Endy = j + GlobalConfig.maxtilecount < Arg.Endy ? j + GlobalConfig.maxtilecount : Arg.Endy + 1;area.Posx = m;area.Posy = n;i += GlobalConfig.maxtilecount;results.Add(area);}j += GlobalConfig.maxtilecount;}return results;}

主下载拼接函数主要有以下任务:一是拆分任务,二是根据任务大小,创建一个bitmap,三是循环x和y下载图,并根据坐标绘制在bitmap上,四是保存bitmap到本地磁盘。其中,在三中还完成了复合图的制作,代码如下。

public void DoWork(object arg){WorkArg Arg = (WorkArg)arg;FileInfo fInfo=new FileInfo(Arg.Filename);string savedir = "";int xNum = (Arg.Endx - Arg.Startx + 1), yNum = (Arg.Endy - Arg.Starty + 1);int totalnum=xNum*yNum,count = 0;//受GDI的限制,每次拼图不能超过20000像素×20000像素,但实际操作过程中,远不止这个数,有时候10000像素也会出错,为了程序的稳定性,//如果设置单幅图最大不超过20×20个瓦片,就是5120×5120像素//此处需要对数据进行拆分List<AreaConfig> tasks = GetAreas(Arg);for (int taski = 0; taski < tasks.Count; taski++){int xnum = tasks[taski].Endx - tasks[taski].Startx, ynum = tasks[taski].Endy - tasks[taski].Starty;Bitmap bit = new Bitmap(xnum * 256, ynum * 256);Graphics g = Graphics.FromImage(bit);for (int i = tasks[taski].Startx, m = 0; i < tasks[taski].Endx; i++, m++){for (int j = tasks[taski].Starty, n = 0; j < tasks[taski].Endy; j++, n++){if (!iswork)break;foreach (var url in Arg.Downloadurls){string durl = Tools.getUrl(url, i, j, Arg.Zoom);byte[] bts = Tools.downloadTile(durl);if (bts == null){g.DrawString("此区域请求失败", new Font("宋体", 12f), new SolidBrush(Color.Red), new Point(m * 256, n * 256));}else{MemoryStream ms = new MemoryStream(bts);Image img = Bitmap.FromStream(ms);g.DrawImage(img, m * 256, n * 256, 256, 256);}}count++;myWorkPercent(count, totalnum,taski+1,tasks.Count);}if (!iswork)break;}string filename = fInfo.DirectoryName + "\\" + fInfo.Name + "\\";if (!Directory.Exists(filename))Directory.CreateDirectory(filename);savedir = filename;if (tasks.Count > 1){filename += tasks[taski].Posy + "-" + tasks[taski].Posx + "_";}filename+=fInfo.Name;bit.Save(filename, ImageFormat.Jpeg);g.Dispose();bit.Dispose();}myFinishWork(savedir);}

最后,启动线程:

public void StartWork(){iswork = true;th = new Thread(new ParameterizedThreadStart(DoWork));th.Start(this.workarg);}

增加2个事件代理,分别传回工作状态和合并完成状态:

    public delegate void Workpercent(int curnum, int maxnum, int ctasknum, int tasknum);public delegate void FinishWork(string filedir);

合并完成后,通知主界面打开任务文件夹:

  void dlw_myFinishWork(string savedir){MessageBox.Show("任务完成!");enableControls(true);System.Diagnostics.Process.Start("explorer.exe", savedir);dlw = null;}

至此,所有工作基本完成。最后在啰嗦3点:
1.最好使用天地图,占位高,不足就是每天数量有限制,可以自己申请开发者的tk,改config.txt后下载。
2.国外图只作为参考,边境问题敏感,立场要坚定,使用要谨慎。
3.软件源码会附在本文后,大家可以下载,共同研究。
链接:https://pan.baidu.com/s/1dGzf3GRY50cqmBL-oA-PGA
提取码:v77a
复制这段内容后打开百度网盘手机App,操作更方便哦
本章参考:
芒果香蕉_《地图瓦片编号与经纬度的换算关系》简书

打造地图拼接利器(五)地图采集与拼接相关推荐

  1. 全景视频拼接(五):将拼接后的图片合成视频

    项目要求:利用双摄像头同时采集两个视频,离线拼接,将两个视频拼接成一个视频. 该部分代码实现功能:将拼接后的图片(有序列)拼接成视频 代码: #include<opencv2\opencv.hp ...

  2. 打造地图拼接利器(二)软件框架

    我们采用Visual studio2013作为开发环境,GMap.net作为地图环境,实现相关功能. 一.GMap.net GMap.NET 是一个免费.开源的.NET控件,有Windows Form ...

  3. 地图也可以玩“私人订制”,打造属于你的专属地图

    电子地图普及至今 仅仅方便全面的查询服务已然不够 要满足用户挑剔的口味 还得有点可塑性 个性化地图就是可以调整地图风格.显示元素等,如下图,同一个位置在不同风格下的样子 ▶▶个性化地图功能就是可以让你 ...

  4. 小白学习Basemap气象画地图的第五天(读取micaps站点数据,省级能见度分布)

    小白学习Basemap气象画地图的第五天(读取micaps站点数据,省级能见度分布) 这一帖子,主要介绍了三个重点: 1.micaps站点数据的读取 2.站点数据的插值 3.不均匀色标的生成 在下面的 ...

  5. python用cartopy包画地图_python绘制地图的利器Cartopy使用说明

    python绘制地图一般使用Basemap绘图包,但该包配置相对较繁琐,自定义性不强,这里介绍一个绘制地图的利器Cartopy,个人认为该工具方便.快捷,附上一些自己写的程序. 准备工作,工欲善其事, ...

  6. 把通过高德静态地图API获取的地图图片,拼接成一张大地图图片

    默认采用: zoom=17&size=1024*1024&scale=2 高德地图api参数.获取的是2048*2048大小的图片,修改参数之后需要调整图片截取和拼接的偏移量 程序中部 ...

  7. python绘制地图地图cartopy_python绘制地图的利器Cartopy使用说明

    python绘制地图一般使用Basemap绘图包,但该包配置相对较繁琐,自定义性不强,这里介绍一个绘制地图的利器Cartopy,个人认为该工具方便.快捷,附上一些自己写的程序. 准备工作,工欲善其事, ...

  8. SRPG游戏开发(五十二)第十一章 地图动作与地图事件 - 一 初始化地图动作(Initialize Map Action)

    返回总目录 第十一章 地图动作与地图事件(Map Action and Map Event) 我们已经有了剧本,而且可以运行剧本,但我们还缺少对地图的操作控制. 我们这一章来完成地图上的操作,地图的操 ...

  9. arcengine 加载地图不显示_地图建筑建模制作与输出

    导读 阅读完此文,你会了解: 1.地图建筑模型通常如何制作的 2.地图建筑模型替换策略 地图上往往会有一些定制建筑的需求,例如将下面的水立方做成气泡感的. 加入定制模型之前 加入定制模型之后 这种需求 ...

  10. 百度地图API和高德地图API资料集锦

    [高德地图API]从零开始学高德JS API(五)路线规划--驾车|公交|步行 [高德地图API]从零开始学高德JS API(四)搜索服务--POI搜索|自动完成|输入提示|行政区域|交叉路口|自有数 ...

最新文章

  1. Python如何实现24个微信大群万人同步转发直播?
  2. unity拖拽 到固定位置_Unity3D拖动任意对象GameObject移动到任意地方
  3. Genymotion模拟器
  4. 【Python基础】分享5 款超牛逼的 Jupyter Notebook 插件!
  5. 爬虫的步骤解析内容xpath介绍_爬虫入门到精通-网页的解析(xpath)
  6. 网络编程释疑之:TCP协议的“流”特性
  7. Mysql 会导致锁表的语法
  8. php实现git服务器,如何搭建和配置Git服务器
  9. 深入Session2
  10. Python(应用) — Tesseract图片文字识别(一)
  11. 微软ime日文输入法每次切换英文和假名输入状态时,画面中央总有图片提示,怎么消掉
  12. 业务侧-到底应该怎么做A/Btest
  13. 又是一年深秋时--西湖枫叶随拍
  14. java实训意义_java实习心得体会
  15. 如何用CNN玩转AlphaGo版的五子棋?
  16. 小猫爪:i.MX RT1050学习笔记26-RT1xxx系列的FlexCAN详解
  17. 巨头“围攻”之下,新氧医美能否“破局”?
  18. 番茄工作法—— pomotime软件使用说明
  19. 蓝纹奶酪的全球与中国市场2022-2028年:技术、参与者、趋势、市场规模及占有率研究报告
  20. echarts柱状图数据显示

热门文章

  1. 复信号在信号处理中的意义
  2. 局域网入侵教程_黑客常用Linux 入侵工具:可获取目标浏览图片的EtterCap
  3. 3dmax 视频全集
  4. 目前我国网络安全人才市场状况
  5. Linux笔记:开机自动运行程序
  6. linux设置进程开机启动,Linux应用程序开机自动启动设置方法
  7. 省选+NOI 第九部分 博弈论
  8. 计算机组装与维修第3版,计算机组装与维护(第3版)
  9. GoldenDict音标乱码
  10. android 静默安装实现,Android 中静默安装实现详解