一文入门GDAL

31 篇文章 8 订阅

GDAL主要用于读取&转换数据。

一 Raster Drivers

GDAL提供了一系列对栅格文件类型的驱动,常见的如BMP、GIF、JPEG、PNG等,详见官网,不全部列出。

二 Vector Drivers

GDAL也提供了一系列对矢量文件类型的驱动,常见的如CAD、CSV、DWG、GeoJSON、MySQL、PostgreSQL等,详见官网,不全部列出。

三 Raster Data Model

下面介绍GDAL关于栅格数据读取的部分抽象数据结构(相关类)。

3.1 DataSet

处于最顶层的是数据集,在GDAL中,使用***GDALDataset***表示,一个数据集表示一组相关的栅格波段的集合和各波段所具有的相同的信息,同时,数据集还负责所有波段的地理参考转换和坐标系统定义,此外,数据集中也有一些元数据之类的东西。

3.2 Coordinate System

GDAL使用OGC WKT数据格式描述数据集的坐标系统,在类***OGRSpatialReference***中进行定义。

  • 使用WKT描述地理坐标系:
  • GEOGCS
    [
        // 坐标系统名称
        "GCS_WGS_1984", 
        // "D_WGS_1984"是大地基准面
        DATUM["D_WGS_1984",SPHEROID["WGS_1984",6378137.0,298.257223563]],
        PRIMEM["Greenwich",0.0],
        UNIT["Degree",0.0174532925199433],
        // EPSG编码
        AUTHORITY["EPSG",4326]
    ]
    
    3.3 坐标转换

    对于栅格平面上的屏幕坐标与地理坐标系中的地理坐标的转换,GDAL提供了两种方式,第一种是仿射变换,第二种是***GCPs***。

  • typedef struct
    {
        char        *pszId;
        char        *pszInfo;
        double      dfGCPPixel;
        double      dfGCPLine;
        double      dfGCPX;
        double      dfGCPY;
        double      dfGCPZ;
    } GDAL_GCP;
    
    3.4 GDALRasterBand

    存储栅格数据的各个波段信息。


    五 Vector Data Model

    GDAL中使用OGR进行对矢量数据的读取,事实上,OGR是基于SFS即简单要素规范的。

    5.1 OGRGeometry

    相应于SFS规范,OGRGeometry中封装了地理坐标信息,也提供了一些几何操作,并能转换为WKT or WKB数据格式,同时,类中还包含着空间坐标参考系统。

    OGRGeometry含有众多子类,如OGRPointOGRLineStringOGRPolygon、 OGRGeometryCollectionOGRMultiPolygonOGRMultiPointOGRMultiLineString

    OGRGeometry的派生关系如下:

    vector structS

    这一数据类型严格符合[OGC SFS][https://blog.csdn.net/qq_40996400/article/details/103731473]的第一部分简单要素模型。

    5.2 OGRSpatialReference

    存储坐标参考,上面有讲,这是栅格和矢量共有的。

    5.3 OGRFeature & OGRFeatureDefn

    OGRGeometry用于定义OGRFeature的几何形状,保存位置信息和坐标参考等,从OGR 1.11开始,一个OGRFeature可以包含多个OGRGeometry,同时,OGRFeature还包含着属性信息,属性信息用OGRFeatureDefn定义,OGRFeatureDefn可以使用OGRLayer中的方法获得。

    5.4 OGRLayer

    示数据源中的一层特征要素,OGRLayer中的所有OGRFeature共用一个公共的数据库也就是具有相同的OGRFeatureDefn。

    5.6 GDALDataset

    矢量数据模型的基类,包含着OGRLayer的集合,通常代表单个文件、一组文件、数据库等。

    GDALDataset是一个抽象基类,需要使用GDALDriver实例化GDALDataset对象。删除GDALDataset会关闭对底层持久数据源的访问,但不会删除该文件。

    GDALDataset还支持SQL。

    5.7 GDALDriver

    用于打开数据文件,返回GDALDataset实例化对象。

    ***********观察栅格数据模型和矢量数据模型,我们可以发现两者的最终基类都是GDALDataset(以往矢量的基类是OGRDatasource),这实现了栅格和矢量的统一。

    以GDAL读取shapefile为例,我们使用GDAL的驱动GDALDriver打开[shapefile]返回GDALDataset实例化对象,GDALDataset中存储着shapefile中的空间信息和非空间属性信息,使用OGRDatasetGetLayer方法获得OGRLayer对象,该对象存储着很多地理要素,通过OGRLayerGetNextFeature方法获得OGRFeature对象,该对象存储着空间信息和非空间属性信息,其中空间信息存储在OGRGeometry对象之中,非空间信息使用GetFieldAsXXX方法获得,通过OGRLayerGetLayerDefn方法获得OGRFeatureDefn对象,该对象存储图层的元数据信息,通过GetFieldDefn方法可以获得每个字段的描述对象OGRField,通过该对象的GetNameRef方法可以获得属性的名称。

    形象地说,我们可以将GDALDataset看作一个数据库,OGRLayer就是数据库中的许多表,OGRFeatureDefn是描述表的元数据也就是表头的信息(除了空间字段),OGRFeature是表中的每一行,每一行有一个或多个空间信息字段,有若干个非空间信息字段。

    下面是一个GDAL读取shp数据的参考代码:

  • void readShape(const char* filename) {
    	// 注册
    	GDALAllRegister();
    	// 解决中文路径
    	CPLSetConfigOption("GDAL_FILENAME_IS_UTF8", "NO");
    	// 解决中文乱码问题
    	CPLSetConfigOption("SHAPE_ENCODING", "");
    	// 读取
    	GDALDataset* dataset = (GDALDataset*)GDALOpenEx(filename, GDAL_OF_VECTOR, NULL, NULL, NULL);
    	if (dataset == NULL)
    	{
    		printf("Open failed.\n");
            GDALClose(dataset);
    		exit(1);
    	}
    	// 图层类对象
    	OGRLayer* poLayer;
    	poLayer = dataset->GetLayer(0); //读取层
    	// 要素描述类对象
    	OGRFeatureDefn* poFeaDefn;
    	poFeaDefn = poLayer->GetLayerDefn();
    	OGREnvelope* envelope = new OGREnvelope;
    	poLayer->GetExtent(envelope);
    	// 要素类对象
    	OGRFeature* poFeature;
    	int n = poFeaDefn->GetFieldCount(); //获得字段的数目,不包括前两个字段(FID,Shape);
    	// 确保是从开始读的
    	poLayer->ResetReading();
    	int i = 0;
    	double width = abs(geolayer->getRect().width());
    	double height = abs(geolayer->getRect().height());
    	while ((poFeature = poLayer->GetNextFeature()) != NULL)
    	{
    		OGRGeometry* geo = poFeature->GetGeometryRef();
    		if (geo != NULL
    			&& wkbFlatten(geo->getGeometryType()) == wkbPoint)
    		{
    			OGRPoint* poPoint = (OGRPoint*)geo;
    		}
    		else if (geo != NULL
    			&& wkbFlatten(geo->getGeometryType()) == wkbPolygon)
    		{
    			OGRPolygon* polygon = (OGRPolygon*)geo;
    			OGRLinearRing* ring = polygon->getExteriorRing();
    			int pointNum = ring->getNumPoints(); // 点个数
    			OGRRawPoint* points = new OGRRawPoint[pointNum];
    			ring->getPoints(points);
    			for (int i = 0; i < pointNum; i++) {
    				
    			}
    		}
    		else if (geo != NULL
    			&& wkbFlatten(geo->getGeometryType()) == wkbLineString)
    		{
    			OGRLineString* polyline = (OGRLineString*)geo;
    			int pointNum = polyline->getNumPoints(); // 点个数
    			for (int i = 0; i < pointNum; i++) {
    				
    			}
    		}
    		QMap<QString, QString> str;
    		// 设置属性
    		for (int i = 0; i < n; i++)
    		{
    			str.insert(QString::fromLocal8Bit(poFeaDefn->GetFieldDefn(i)->GetNameRef()), QString::fromLocal8Bit(poFeature->GetFieldAsString(i)));
    		}
    		OGRFeature::DestroyFeature(poFeature);
    	}
    	GDALClose(dataset);
    }
    

    然后是一个GDAL读写栅格数据(以png图像为例)的参考代码:

  • void readImg(const char* filename) {
    	// 注册
    	GDALAllRegister();
    	// 数据集
    	GDALDataset* poDataset;
    	// 解决中文路径
    	CPLSetConfigOption("GDAL_FILENAME_IS_UTF8", "NO");
    	// 解决中文乱码问题
    	CPLSetConfigOption("SHAPE_ENCODING", "");
    	// 读取
    	poDataset = (GDALDataset*)GDALOpenEx(filename, GDAL_OF_RASTER, NULL, NULL, NULL);
    	if (poDataset == NULL)
    	{
    		cout << "数据集为空!" << endl;
    		GDALClose(poDataset);
    		return;
    	}
    	// 图像宽度
    	double width = poDataset->GetRasterXSize();
    	// 图像高度
    	double height = poDataset->GetRasterYSize();
    	// 图像数据存储二维数组
    	unsigned char* imageData = { 0 };
    	// 图像栅格波段
    	GDALRasterBand* band;
    	// 波段总个数
    	int rasterCount = poDataset->GetRasterCount();
    	if (rasterCount == 1) {// 只有一个波段,为灰度图
    		band = poDataset->GetRasterBand(1);
    		imageData = new unsigned char[width * height];
    		band->RasterIO(GF_Read, 0, 0, width, height, imageData, width, height, GDT_Byte, rasterCount, rasterCount*width);
    	}
    	else if (rasterCount == 4||rasterCount==3) {// 有三or四个波段,输出彩色图像 RGBA->ABGR
    		imageData = new unsigned char[rasterCount * width * height];
    		for (int i = 0; i < rasterCount; i++) {
    			unsigned char* imageOffset = imageData + i ;
    			band = poDataset->GetRasterBand(rasterCount - i ); // 从后向前读取
    			band->RasterIO(
    				GF_Read,	// 读取模式
    				0, 0,		// 读取起始位置,(0,0)表示左上角
    				width, height, // 读取区域大小
    				imageOffset,	// 读取数据或写入数据的缓冲区。这个缓冲区必须至少包含eBufType类型的nBufXSize * nBufYSize的大小,它是按照从左到右,从上到下的像素顺序组织的,间距由nPixelSpace和nLineSpace参数控制。
    				width, // 所需区域读入的缓冲区图像的宽度,nBufXSize
    				height,	// 所需区域读入的缓冲区图像的高度,nBufYSize
    				GDT_Byte, // 存储类型eBufType
    				rasterCount, // 在扫描线中,从pData中的一个像素值开始到下一个像素值开始的字节偏移量。如果默认值为0,则使用 数据类型eBufType的大小
    				rasterCount*width); // 从一个扫描行开始到下一个扫描行开始的字节偏移量。如果默认值为0,则使用 数据类型eBufType*nBufXSize的大小。
    		}
    	}
    	// 输出图像
    	char* fileName = (char*)("G:\\kmj\\实习\\images\\my.png");
    	cout << sizeof(GDT_Byte);
    	writeImage(fileName,imageData,width,height,rasterCount);
    	GDALClose((GDALDatasetH)poDataset);
    }
    
    bool writeImage(char* filename, unsigned char* imageData, int width, int height, int rasterCount)
    {
    	assert(!(filename == NULL || imageData == NULL || width < 1 || height < 1 || rasterCount < 1));
    
    	GDALAllRegister();
    	// 判断图像类型
    	const char* GType = getImageType(filename);
    	if (GType == NULL) { return false; }
    
    	// 查找MEM存储栅格驱动
    	GDALDriver* pMemDriver = NULL;
    	pMemDriver = GetGDALDriverManager()->GetDriverByName("MEM");
    	if (pMemDriver == NULL) { return false; }
    
    	GDALDataset* pMemDataSet = pMemDriver->Create("", width, height, rasterCount, GDT_Byte, NULL);
    	GDALRasterBand* pBand = NULL;
    	unsigned char* ptr1 = (unsigned char*)imageData;
    	for (int i = 0; i < rasterCount; i++)
    	{
    		pBand = pMemDataSet->GetRasterBand(rasterCount - i );
    		pBand->RasterIO(GF_Write,
    			0,
    			0,
    			width,
    			height,
    			ptr1 + i ,
    			width,
    			height,
    			GDT_Byte,
    			rasterCount,
    			rasterCount*width);
    	}
    
    	GDALDriver* pDstDriver = NULL;
    	pDstDriver = (GDALDriver*)GDALGetDriverByName(GType);
    	if (pDstDriver == NULL) { return false; }
    
    	pDstDriver->CreateCopy(filename, pMemDataSet, FALSE, NULL, NULL, NULL);
    	GDALClose(pMemDataSet);
    
    	return true;
    }
    
    const char* getImageType(char* filename)
    {
    	char* extension = strrchr(filename, '.') + 1;
    	const char* Gtype = NULL;
    	if (0 == strcmp(extension, "bmp")) Gtype = "BMP";
    	else if (0 == strcmp(extension, "jpg")) Gtype = "JPEG";
    	else if (0 == strcmp(extension, "png")) Gtype = "PNG";
    	else if (0 == strcmp(extension, "tif")) Gtype = "GTiff";
    	else if (0 == strcmp(extension, "gif")) Gtype = "GIF";
    	else Gtype = NULL;
    	return Gtype;
    }
    

  • 16
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值