使用GDAL库读写SHP文件

       本文介绍如何读写shp文件的矢量线。shape文件包括一个主文件,一个索引文件,和一个dBASE表,其中主文件的后缀就是.shp,图形索引文件的后缀是.shx,dBASE表的后缀是.dbf,每个图形的属性数据存储在dBase格式的数据表之中。其它的文件还包括:Shapefile空间索引格式(.sbn),这是一个二进制的空间索引文件,仅仅可以应用在ESRI的软件之中。其文件格式没有公开的文档,其他厂商也没有实现这个文件。.sbn并不是必须的,因为.shp文件之中已经包含了所有的解析空间数据所需的信息。

在项目中,我们经常需要提取一些道路标线等对象保存为shp文件。本文使用到了Eigen库,需要的同学自己下载使用。

使用GDAL读写shp需要注意以下的问题:

(1)为了支持中文路径,在注册了驱动之后,加上第三句就可以了。必须设置为“NO”。

GDALAllRegister();
OGRAllRegister();
CPLSetConfigOption("GDAL_FILENAME_IS_UTF8","NO");

(2)为了支持Shp中的中文属性输入,在注册了驱动之后,也得加上一句。 

CPLSetConfigOption( "SHAPE_ENCODING", "" );

有时候,也可以指定函数第二个选项为“CP936”

CPLSetConfigOption("SHAPE_ENCODING","CP936");

(3)使用double或者float类型作为属性值输入,需要设置字段的宽度:

	// create the attributes tables:
	OGRFieldDefn poFieldID("ID", OFTInteger);
	OGRFieldDefn oFieldx("x坐标", OFTReal);
	OGRFieldDefn oFieldy("y坐标", OFTReal);
	OGRFieldDefn oFieldz("z坐标", OFTReal);

	poFieldID.SetWidth(40);
	oFieldx.SetPrecision(8);
	oFieldy.SetPrecision(8);
	oFieldz.SetPrecision(8);

(4)版本问题:在GDAL 2.0+的C/C++版本中移除了对于OGRDatasource及OGRSFDriver的支持。分别用GDALDataset、GDALDriver代替,类下的方法不变。

GDAL2.0以下版本读取矢量线shp文件:

bool readPolylineShp(const std::string &filename, std::vector> &pts) 
{
    // if this file exist, else exit!
    if((_access(filename.c_str(), 0)) != -1)
	{
        std::cout << "This .shp file exist." << filename << std::endl;
        //注册所有的文件格式驱动,这个函数注册了GDAL/OGR支持的所有格式
        OGRRegisterAll();
        //下一步我们将打开输入的OGR数据文件。数据文件可以是文件,关系型数据库,文件路径,甚至可能是远程的网络服务,这点取决于我们使用的驱动。但是,数据源的名字通常只是一个简单的字符串。既然这样拿我们就编写一个打开shapefile的程序。第二个参数(FLALSE)告诉OGRSFDriverRegistrar::Open() 函数我们不需要update access。如果失败返回NULL,并报错。
        OGRDataSource *poDS;
        poDS = OGRSFDriverRegistrar::Open(filename.c_str(), TRUE);
        if(poDS == NULL)
		{
            std::cout << "Failed in opening adding shp file;" << std::endl;
            OGRDataSource::DestroyDataSource(poDS);
            exit(1);
        }
        //一个OGRDataSource可能包含很多的层。所包含层的数量我们可以用过调        OGRDataSource::GetLayerCount()得到,并且其中每一个曾我们利用索引调用OGRDataSource::GetLayer()得到。也可以利用层的名字得到。
        int layerN = poDS->GetLayerCount();
        //std::cout << "layer count is: " << layerN << std::endl;
        OGRLayer *poLayer;
        poLayer = poDS->GetLayer(0);
        if(poLayer == NULL)
		{
            std::cout << "Failed in getting layer in existing .shp file" << std::endl;
            OGRDataSource::DestroyDataSource(poDS);
            return false;
        }

        // get vertexes on lines
        std::vector>().swap(pts);

        //自从我们开始fresh with这个层,就没有这么严格了。很明智地我们需要调用Layer::ResetReading()来确保我们是从层的开头开始。
        poLayer->ResetReading();
        // 现在我们开始读取层里面的features。在开始之前我们需要指定一个attribute或者spatial filter来严格控制我们得到的feature。不过现在我们只是得到所有的features。
        OGRSpatialReference *shp_spf;
        shp_spf = poLayer->GetSpatialRef();     // geographical projection information

        //
        int iFeatureCount = poLayer->GetFeatureCount();
        pts.resize(iFeatureCount);

        OGRFeature *poFeature=NULL;
        // traverse all features in the layer:我们不断地调用OGRLayer::GetNextFeature()函数来遍历所有的features,当遍历完所有的features后返回NULL。
        int flag_line = 0;
        while ((poFeature = poLayer->GetNextFeature()) != NULL)
		{
            //feature里面提取出几何(geometry)数据
            OGRGeometry *poGeometry = poFeature->GetGeometryRef();
            OGRwkbGeometryType geotype;
            geotype = poGeometry->getGeometryType();
            OGRPoint *poPoint = NULL;
            OGRLineString *poLine = NULL;
            //确定这个几何数据的类型,如果是点,我们将他标为点并且进行操作,如果是其他的内省我们write占位符。这里的类型是多段线
            if(wkbLineString25D == geotype)
			{
                poLine = (OGRLineString*)poGeometry;
                int pNUM = poLine->getNumPoints();
                for(int i = 0; i < pNUM; ++i)
				{
                    Eigen::Vector3f temp_pt;
                    temp_pt[0] = poLine->getX(i);
                    temp_pt[1] = poLine->getY(i);
                    temp_pt[2] = poLine->getZ(i);
                    pts[flag_line].push_back(temp_pt);
                }
            }
            flag_line++;
            delete  poPoint;
            delete poLine;
        }
        //资源清理
        GDALClose(poDS);
        return true;
    }
	else
	{
        std::cout << "This .shp file is not exist!" << std::endl;
        return false;
    }
}

GDAL2.0以下版本写矢量线shp文件:

bool WriteShp(string filename)
{
	//创建
	const char *pszDriverName = "ESRI Shapefile";
	OGRSFDriver *poDriver;
	OGRRegisterAll();

	poDriver = OGRSFDriverRegistrar::GetRegistrar()->GetDriverByName(pszDriverName);
	if (poDriver == NULL)
	{
		std::cout << pszDriverName << " : This driver is not available!" << std::endl;
		exit(1);
	}
	
	OGRDataSource* poDS;
	if ((_access(filename.c_str(), 0)) != -1)
	{
		std::cout << "Exist this shp file." << std::endl;
		remove(filename.c_str());
	}
	poDS = poDriver->CreateDataSource(filename.c_str(), NULL);
	if (poDS == NULL)
	{
		std::cout << "Failed in creating layers" << std::endl;
		exit(1);
	}
	// create layers:
	OGRLayer *poLayer;
	//poLayer = poDS->CreateLayer("curbs3D", NULL, wkbMultiLineString25D, NULL);
	poLayer = poDS->CreateLayer("point_out", NULL, wkbPoint, NULL);
	if (poLayer == NULL)
	{
		std::cout << "Layer creation failed!" << std::endl;
		exit(1);
	}


	// create the attributes tables:
	OGRFieldDefn poFieldID("ID", OFTInteger);
	poFieldID.SetWidth(40);
	if (poLayer->CreateField(&poFieldID) != OGRERR_NONE)
	{
		std::cout << "ID field creation failed!" << std::endl;
		exit(1);
	}
	OGRFieldDefn oField("Name", OFTString);
	oField.SetWidth(32);
	if (poLayer->CreateField(&oField) != OGRERR_NONE)
	{
		printf("Creating Name field failed.\n");
		exit(1);
	}
	//下面从stdin中循环读取  “x,y,name”格式的点信息。
	double x = 0, y = 100;
	char szName[33];
	for (int a = 0; a != 10; ++a)
	{
		x += a;
		y += a;
		OGRFeature *poFeature;
		poFeature = new OGRFeature(poLayer->GetLayerDefn());
		poFeature->SetField("Name", szName);
		OGRPoint pt;
		pt.setX(x);
		pt.setY(y);
		poFeature->SetGeometry(&pt);
		if (poLayer->CreateFeature(poFeature) != OGRERR_NONE)
		{
			cout << "创建要素失败" << endl;
		}
		//OGRFeature::DestroyFeature(*poFeature);//创建一个feature.OGRLayer::CreateFeature() 只是重新复制了一个feature,因此操作完成后需要清除feature对象
	}
	OGRDataSource::DestroyDataSource(poDS);

	return true;
}

 

你可能感兴趣的:(C++,GDAL)