GDAL基本介绍
- GDAL全称是Geospatial Data Abstraction Library(地理空间数据抽象库),是使用C/C++语言编写的用于读写空间数据的一套跨平台开源库,它利用抽象数据模型来表达所支持的各种文件格式,还使用一系列命令行来进行数据转换和处理。现有的大部分GIS或者遥感平台,不论是商业软件ArcGIS,ENVI还是开源软件GRASS,QGIS,都使用了GDAL作为底层构建库。
- GDAL最初是由Frank Warmerdam于1998年开始开发的,在GDAL1.3.2版本之后,正式有开源空间信息基金会(Open Source Geospatial Foundation,OSGeo)下的GDAL/OGR项目委员会对其进行维护。
- GDAL库由OGR和GDAL项目合并而来,OGR主要用于空间要素矢量矢量数据的解析,GDAL主要用于空间栅格数据的读写。此外,空间参考及其投影转换使用开源库 PROJ.4进行。
- 目前,GDAL主要提供了三大类数据的支持:栅格数据,矢量数据以及空间网络数据(Geographic Network Model)。栅格数据格式包括Arc/Info ASCII Grid(ASC),GeoTiff(Tiff),Erdas Imagine Images(Img),ASCII DEM(DEM)等。矢量数据格式主要包括ESRI Shapefiles、S-57、SDTS、PostGIS、Oracle Spatial、Mapinfo mid/mif和Mapinfo TAB等。
- GDAL提供了C/C++接口,并且通过SWIG提供了Python,Java,C#等的调用接口。当我们在Java中调用GDAL的API函数时,其实底层执行的是C/C++编译的二进制文件。
GDAL 前置GIS基础
1. 栅格数据
图片格式的 GIS 数据我们统称为栅格数据,在 GIS 领域里,一切的图片,图像,影片,影像格式的数据都称作是栅格数据(图像数据)。
2. 矢量数据
矢量数据是指一些人工手绘的、测绘而得来的一些图形数据或者是由栅格数据中得出。这种数据往往只有一些简单的轮廓构成,比如一个行政区可能只用轮廓边界表示,一条河流可能只用一条线段表示。这就是我们的矢量数据(图形数据)。
GIS 开发过程中,矢量数据的主要格式有 shape 格式和 geojson 格式,wkt格式。一种是文件形式,一种是网络传输中的json形式,wkt是一种精简化数据格式,只记录地理数据,不记录属性数据;
3. 坐标系和投影
-
在地理学中,坐标系分为地理坐标系和投影坐标系。
-
地理坐标系又称为经纬度坐标系。地理坐标系的核心就是用经纬度来表示位置。不管是在球面上还是把球面投影到平面上,如果你想用地理坐标系来表示一幅地图的任意位置,那这个位置的表示方式一定是经纬度。投影坐标系是指基于地理坐标系进行的一些投影变换,投影坐标系的单位通常是米(m)或者千米,即距离长度为单位。每个地理坐标系都可以通过数据计算和变换产生出很多的投影坐标系。
-
在国内,常用的地理坐标系有两个,分别是 wgs84 坐标系和 CGCS2000 坐标系。常用的投影坐标系有很多,最主要的是 web 墨卡托投影坐标系,这个坐标系是经过 wgs84 坐标系投影而来的。
-
在实际开发过程中坐标系经常用EPSG 代码来表示,EPSG 是欧洲一个石油组织的简称,这个组织提出了一种规范,用代号的形式来表示坐标系。 wgs84 坐标系我们会表示成 EPSG:4326。CGCS2000 坐标系我们会表示成 EPSG:4490。web 墨卡托坐标系我们会表示成 EPSG:900913 或者 EPSG:3857 等等。所以当在开发的过程中遇到了这些代号,需要做一个转换时,清楚其要表示的坐标系即可。
GDAL体系结构
GDAL体系
- GDAL使用抽象数据模型(abstract data model)来解析它所支持的数据格式,抽象数据模型包括数据集(dataset),坐标系统,仿射地理坐标转换(Affine Geo Transform), 大地控制点(GCPs), 元数据(Metadata),栅格波段(Raster Band),颜色表(Color Table),子数据集域(Subdatasets Domain),图像结构域(Image_Structure Domain),XML域(XML:Domains)。
- GDALMajorObject类:带有元数据的对象。
- GDALDdataset类:通常是从一个栅格文件中提取的相关联的栅格波段集合和这些波段的元数据;GDALDdataset也负责所有栅格波段的地理坐标转换(georeferencing transform)和坐标系定义。
- GDALDriver类:GDAL会为每一个所支持的文件格式创建一个该类的实体,来管理该文件格式。
- GDALDriverManager类:用来管理GDALDriver类。
OGR体系
- Geometry类:Geometry (包括OGRGeometry等类)封装了OpenGIS的矢量数据模型,并提供了一些几何操作,WKB(Well Knows Binary)和WKT(Well Known Text)格式之间的相互转换,以及空间参考系统(投影)。
- Spatial Reference类:OGRSpatialReference封装了投影和基准面的定义。
- Feature类:OGRFeature封装了一个完整feature的定义,一个完整的feature包括一个geometry和geometry的一系列属性。
- Feature Definition类:OGRFeatureDefn里面封装了feature的属性,类型、名称及其默认的空间参考系统等。一个OGRFeatureDefn对象通常与一个层(layer)对应。
- Layer类:OGRLayer是一个抽象基类,表示数据源类OGRDataSource里面的一层要素(feature)。
- Data Source类:OGRDataSource是一个抽象基类,表示含有OGRLayer对象的一个文件或一个数据库。
- Drivers类:OGRSFDriver对应于每一个所支持的矢量文件格式。类OGRSFDriver由类 OGRSFDriverRegistrar来注册和管理。
GDAL的快速上手
本地开发环境的配置
参考gdal的配置文档即可
demo1:对tiff的读取
- 使用GDAL第一步,注册gdal驱动
import org.gdal.gdal.gdal;
gdal.AllRegister();
- 读取数据源信息,以只读的方式打开文件
//载入栅格,读取相关信息
Dataset rasterDataset = gdal.Open("D:\\gdal\\class.tif", gdalconstConstants.GA_ReadOnly);
此时,Tiff文件中的数据已经读到了rasterDataset中,后续的数据操作将围绕rasterDataset进行。
- 最后需要释放资源
rasterDataset.delete();
- 完整代码
public static void main(String[] args) {
String inRaster = "D:\\gdal\\class.tif";
String outShp = "D:\\gdal\\class.shp";
//注册驱动,否则下边执行报错
gdal.AllRegister();
//载入栅格,读取相关信息
Dataset rasterDataset = gdal.Open(inRaster, gdalconstConstants.GA_ReadOnly);
//栅格的波段信息
Band band = rasterDataset.GetRasterBand(1);
//设置坐标系
SpatialReference prj = new SpatialReference();
prj.ImportFromEPSG(4326);
//释放资源
rasterDataset.delete();
}
demo2:对shp数据的读取
-
ogr全局注册和全局配置
// 指定文件的名字和路径 String strVectorFile = "F:\\vector_data\\道路中心线.shp"; // 注册所有的驱动 ogr.RegisterAll(); // 为了支持中文路径,请添加下面这句代码 gdal.SetConfigOption("GDAL_FILENAME_IS_UTF8", "YES"); // 为了使属性表字段支持中文,请添加下面这句 gdal.SetConfigOption("SHAPE_ENCODING", "CP936");
-
获取shp驱动,打开shp文件
// 读取数据,这里以ESRI的shp文件为例 String strDriverName = "ESRI Shapefile"; // 创建一个文件,根据strDriverName扩展名自动判断驱动类型 org.gdal.ogr.Driver oDriver = ogr.GetDriverByName(strDriverName); if (oDriver == null) { System.out.println(strDriverName + " 驱动不可用!\n"); return; } DataSource dataSource = oDriver.Open(strVectorFile);
-
获取图层、空间参考、图层范围
//Layer layer = dataSource.GetLayer("test"); Layer layer = dataSource.GetLayer(0); for(int i = 0;i<dataSource.GetLayerCount();i++) { Layer layerIdx = dataSource.GetLayer(i); System.out.println("图层名称:<==>" + layerIdx.GetName()); } String layerName = layer.GetName(); System.out.println("图层名称:" + layerName); SpatialReference spatialReference = layer.GetSpatialRef(); //System.out.println(spatialReference); System.out.println("空间参考坐标系:" + spatialReference.GetAttrValue("AUTHORITY", 0) + spatialReference.GetAttrValue("AUTHORITY", 1)); double[] layerExtent = layer.GetExtent(); System.out.println("图层范围:minx:" + layerExtent[0] + ",maxx:" + layerExtent[1] + ",miny:" + layerExtent[2] + ",maxy:" + layerExtent[3]);
-
获取图层属性信息
FeatureDefn featureDefn = layer.GetLayerDefn(); int fieldCount = featureDefn.GetFieldCount(); Map<String,String> fieldMap = new HashMap<String,String>(); for (int i = 0; i < fieldCount; i++) { FieldDefn fieldDefn = featureDefn.GetFieldDefn(i); // 得到属性字段类型 int fieldType = fieldDefn.GetFieldType(); String fieldTypeName = fieldDefn.GetFieldTypeName(fieldType); // 得到属性字段名称 String fieldName = fieldDefn.GetName(); fieldMap.put(fieldTypeName, fieldName); } System.out.println(fieldMap);
-
读取空间空间信息及属性列表
System.out.println(layer.GetFeature(1).GetGeometryRef().ExportToJson()); System.out.println(layer.GetFeature(2).GetGeometryRef().ExportToJson()); System.out.println(layer.GetFeature(3).GetGeometryRef().ExportToJson()); for (int i = 0; i < featureCount; i++) { Feature feature = layer.GetFeature(i); Object[] arr = fieldMap.values().toArray(); for (int k = 0; k < arr.length; k++) { String fvalue = feature.GetFieldAsString(arr[k].toString()); System.out.println(" 属性名称:" + arr[k].toString() + ",属性值:" + fvalue); } }
结果如下
-
关闭资源
dataSource.delete();
demo3 栅格矢量化
说明:GDAL中内嵌了矢量化的算法,直接调用即可
public static void main(String[] args) {
String inRaster = "D:\\gdal\\class.tif";
String outShp = "D:\\gdal\\class.shp";
//注册驱动,否则下边执行报错
gdal.AllRegister();
//载入栅格,读取相关信息
Dataset rasterDataset = gdal.Open(inRaster, gdalconstConstants.GA_ReadOnly);
//栅格转矢量需要的波段信息
Band band = rasterDataset.GetRasterBand(1);
//设置坐标系
SpatialReference prj = new SpatialReference();
if (rasterDataset.GetProjectionRef().isEmpty()) {
prj.ImportFromEPSG(4326);
} else {
//栅格数据的坐标系作为矢量化后的坐标系
prj.ImportFromWkt(rasterDataset.GetProjectionRef());
}
//创建输出矢量
org.gdal.gdal.Driver driver = gdal.GetDriverByName("ESRI Shapefile");
Dataset shpDataset = driver.Create(outShp, rasterDataset.getRasterXSize(), rasterDataset.getRasterYSize(), 1, gdalconst.GDT_Byte);
Layer layer = shpDataset.CreateLayer("layer", prj);
FieldDefn field = new FieldDefn("value", ogr.OFTInteger);//创建一个字段用来存储栅格的像素值
layer.CreateField(field, 1);
//矢量化
gdal.Polygonize(band, null, layer, 0);
log.info("矢量化过程完成");
layer.SyncToDisk();
//释放资源
shpDataset.delete();
rasterDataset.delete();
}
demo4 叠加之相交分析
说明:GDAL具有很多空间分析功能,感兴趣可以调用研究
public class VectorIntersection {
public static void main(String[] args) {
// 初始化GDAL库
ogr.RegisterAll();
// 加载输入的矢量数据
DataSource src1 = ogr.Open("D:\\gdal\\geo_xzqh_cun_anhui\\GEO_XZQH_CUN.shp", 0);
DataSource src2 = ogr.Open("D:\\gdal\\HS_H09_20240406_0900_anh_new.shp", 0);
if (src1 == null || src2 == null) {
System.out.println("无法打开矢量数据源");
return;
}
// 获取操作的图层
Layer layer1 = src1.GetLayerByIndex(0);
Layer layer2 = src2.GetLayerByIndex(0);
// 设置相交后结果的空间参考
SpatialReference spatialReference = new SpatialReference();
spatialReference.SetWellKnownGeogCS("WGS84");
// 创建输出矢量数据源
Driver driver = ogr.GetDriverByName("ESRI Shapefile");
DataSource dst = driver.CreateDataSource("D:\\gdal\\outlay.shp", null);
// 执行相交操作
layer1.Intersection(layer2, dst.GetLayerByIndex(0));
// 关闭数据源
dst.SyncToDisk();
dst.delete();
src1.delete();
src2.delete();
System.out.println("相交操作完成,结果保存在 'outlay.shp'");