开工之前要先了解一下瓦片地图,瓦片地图金字塔模型是一种多分辨率层次模型,从瓦片金字塔的底层到顶层,分辨率越来越低,但表示的地理范围不变。实现原理就是,首先确定地图服务平台所要提供的缩放级别的数量N,把缩放级别最低、地图比例尺最大的地图图片作为金字塔的底层,即第0层,并对其进行分块,从地图图片的左上角开始,从左至右、从上到下进行切割,分割成相同大小(比如256x256像素)的正方形地图瓦片,形成第0层瓦片矩阵;在第O层地图图片的基础上,按每2x2像素合成为一个像素的方法生成第1层地图图片,并对其进行分块,分割成与下一层相同大小的正方形地图瓦片,形成第1层瓦片矩阵;采用同样的方法生成第2层瓦片矩阵;…;如此下去,直到第N一1层,构成整个瓦片金字塔。
在Google地图中,地图数据由大量的正方形图片组成。共有2级缩放比例,每个地图图片都有坐标值,由X和Y值构成。比例因子zoom取值范围是(0-22)。操作地图滑竿显示更大比例尺地图时,图片的数量发生裂变。两种模式在请求及响应的速度方面有明显的差异,基于地图瓦片服务框架的响应速度要快于传统的WebGIS,同时对地图服务器的负载也相应小一些。
现在可以开工,首先准备好瓦片地图,如果是用来学习研究的话,我们可以通过一些第三方工具(当然自己写一个也可以)从谷歌地图抓取瓦片地图存放到本地,例如以C:\googlemas\satelite\x\y.jpg的格式存放,其中x,y,z分别代瓦片所在的列、行、缩放等级。
有了这些图我们开始拼接了,关于gdal库的介绍的文章很多了,现在我主要是介绍如何用C#来拼接谷歌地图,废话不多说了,直接上代码:
void SaveBitmapBuffered(Dataset src, Dataset dst, int x, int y) { if (src.RasterCount < 3) { System.Environment.Exit(-1); } // Get the GDAL Band objects from the Dataset Band redBand = src.GetRasterBand(1); Band greenBand = src.GetRasterBand(2); Band blueBand = src.GetRasterBand(3); // Get the width and height of the raster int width = redBand.XSize; int height = redBand.YSize; byte[] r = new byte[width * height]; byte[] g = new byte[width * height]; byte[] b = new byte[width * height]; redBand.ReadRaster(0, 0, width, height, r, width, height, 0, 0); greenBand.ReadRaster(0, 0, width, height, g, width, height, 0, 0); blueBand.ReadRaster(0, 0, width, height, b, width, height, 0, 0); Band wrb = dst.GetRasterBand(1); wrb.WriteRaster(x * width, y * height, width, height, r, width, height, 0, 0); Band wgb = dst.GetRasterBand(2); wgb.WriteRaster(x * width, y * height, width, height, g, width, height, 0, 0); Band wbb = dst.GetRasterBand(3); wbb.WriteRaster(x * width, y * height, width, height, b, width, height, 0, 0); } /// <summary> /// 拼接瓦片 /// </summary> /// <param name="tilesBounds"></param> /// <param name="tilePath"></param> /// <param name="outPutFileName"></param> public void CombineTiles(TilesBounds tilesBounds, string tilePath, string outPutFileName) { if (File.Exists(outPutFileName)) { File.Delete(outPutFileName); } int imageWidth = 256 * (tilesBounds.maxCol - tilesBounds.minCol + 1); int imageHeight = 256 * (tilesBounds.maxRow - tilesBounds.minRow + 1); //Register driver(s). Gdal.AllRegister(); Driver driver = Gdal.GetDriverByName("GTiff"); Dataset destDataset = driver.Create(outPutFileName, imageWidth, imageHeight, 3, DataType.GDT_Byte, null);
for (int col = tilesBounds.minCol; col <= tilesBounds.maxCol; col++) { for (int row = tilesBounds.minRow; row <= tilesBounds.maxRow; row++) { try { string sourceFileName = tilePath + tilesBounds.zoomLevel.ToString() + "\\" + col.ToString() + "\\" + row.ToString() + ".jpg"; if (File.Exists(sourceFileName)) { Dataset sourceDataset = Gdal.Open(sourceFileName, Access.GA_ReadOnly); if (sourceDataset != null) { SaveBitmapBuffered(sourceDataset, destDataset, col - tilesBounds.minCol, row - tilesBounds.minRow); } } } catch (Exception ex) { MessageBox.Show(ex.ToString()); } } } destDataset.Dispose(); } //调用 TilesBounds tilesBounds = new TilesBounds(); tilesBounds.minCol = 107901; tilesBounds.maxCol = 107926; tilesBounds.minRow = 49652; tilesBounds.maxRow = 49668; tilesBounds.zoomLevel = 17; string outPutFileName = "c:\\北京卫星图" + tilesBounds.zoomLevel.ToString() + ".tif"; string tilePath = "C:\\googlemaps\\\satelite\\"; CombineTiles(tilesBounds, tilePath, outPutFileName);
ok,大功告成了,看看效果图吧。