GDAL
| Language | C++ with a C API (bindings for Python, Java, C#, Go, Rust) |
|---|---|
| License | MIT |
| Repository | github.com/OSGeo/gdal |
| Documentation | gdal.org, GTiff driver, COG driver |
| Depends on | PROJ, libtiff, libgeotiff (bundled copies available), many optional libraries |
| Read / write | yes / yes, GeoTIFF, BigTIFF, COG, tiles, overviews, all common compressions |
GDAL (Geospatial Data Abstraction Library) is the standard library for raster and vector geodata. QGIS, ArcGIS, PostGIS, rasterio and most GIS software use it. Its GTiff driver reads and writes TIFF with georeferencing: the affine geotransform, the coordinate reference system, nodata values, overviews and metadata. A dataset has one or more bands; each band is a 2D array of a data type such as GDT_Byte, GDT_UInt16 or GDT_Float32.
GDAL is large. If you only need regular images (photos, scans), use libtiff directly.
Installation#
# Debian / Ubuntu
sudo apt install libgdal-dev gdal-bin
# Fedora
sudo dnf install gdal-devel
# macOS
brew install gdal
# Windows (vcpkg)
vcpkg install gdal
With CMake:
find_package(GDAL CONFIG REQUIRED)
target_link_libraries(myapp PRIVATE GDAL::GDAL)
Basic usage: read a GeoTIFF#
#include <gdal_priv.h>
#include <cstdio>
#include <vector>
int main() {
GDALAllRegister();
GDALDataset *ds = GDALDataset::Open("dem.tif", GDAL_OF_RASTER | GDAL_OF_READONLY);
if (!ds) return 1;
int w = ds->GetRasterXSize(), h = ds->GetRasterYSize();
printf("%d x %d, %d bands\n", w, h, ds->GetRasterCount());
double gt[6];
if (ds->GetGeoTransform(gt) == CE_None)
printf("origin %f, %f, pixel size %f, %f\n", gt[0], gt[3], gt[1], gt[5]);
if (const OGRSpatialReference *srs = ds->GetSpatialRef())
printf("CRS: %s\n", srs->GetName());
GDALRasterBand *band = ds->GetRasterBand(1); // bands are 1-based
std::vector<float> row(w);
if (band->RasterIO(GF_Read, 0, 0, w, 1, row.data(), w, 1, GDT_Float32, 0, 0) == CE_None)
printf("first value: %f\n", row[0]);
GDALClose(ds);
return 0;
}
gt[0], gt[3] are the coordinates of the top-left corner; gt[1], gt[5] are the pixel width and (negative) height.
How to create a compressed GeoTIFF#
#include <gdal_priv.h>
#include <ogr_spatialref.h>
#include <cpl_string.h>
#include <vector>
int write_geotiff(const char *path, const std::vector<float> &data, int w, int h) {
GDALAllRegister();
GDALDriver *drv = GetGDALDriverManager()->GetDriverByName("GTiff");
char **opts = nullptr;
opts = CSLSetNameValue(opts, "COMPRESS", "DEFLATE");
opts = CSLSetNameValue(opts, "PREDICTOR", "3"); // floating point predictor
opts = CSLSetNameValue(opts, "TILED", "YES");
GDALDataset *ds = drv->Create(path, w, h, 1, GDT_Float32, opts);
CSLDestroy(opts);
if (!ds) return 1;
double gt[6] = {10.0, 0.001, 0.0, 50.0, 0.0, -0.001}; // origin lon 10, lat 50
ds->SetGeoTransform(gt);
OGRSpatialReference srs;
srs.importFromEPSG(4326);
ds->SetSpatialRef(&srs);
GDALRasterBand *band = ds->GetRasterBand(1);
band->SetNoDataValue(-9999);
CPLErr err = band->RasterIO(GF_Write, 0, 0, w, h, const_cast<float *>(data.data()),
w, h, GDT_Float32, 0, 0);
GDALClose(ds); // flushes and closes the file
return err == CE_None ? 0 : 1;
}
Useful creation options: COMPRESS=LZW|DEFLATE|ZSTD|JPEG|WEBP|LERC, BIGTIFF=YES|IF_SAFER, TILED=YES, BLOCKXSIZE=512.
How to convert a TIFF to a Cloud Optimized GeoTIFF#
The COG driver only supports CreateCopy. It adds tiles, overviews and the right layout:
GDALDataset *src = GDALDataset::Open("in.tif", GDAL_OF_RASTER);
GDALDriver *cog = GetGDALDriverManager()->GetDriverByName("COG");
char **opts = CSLSetNameValue(nullptr, "COMPRESS", "DEFLATE");
GDALDataset *dst = cog->CreateCopy("out_cog.tif", src, FALSE, opts, nullptr, nullptr);
CSLDestroy(opts);
if (dst) GDALClose(dst);
GDALClose(src);
The same from the command line: gdal_translate in.tif out_cog.tif -of COG -co COMPRESS=DEFLATE.
How to read TIFF metadata#
// TIFF tags such as TIFFTAG_SOFTWARE, TIFFTAG_DATETIME, plus GDAL metadata
char **md = ds->GetMetadata();
for (int i = 0; md && md[i]; i++) printf("%s\n", md[i]);
// compression and layout
if (const char *c = ds->GetMetadataItem("COMPRESSION", "IMAGE_STRUCTURE"))
printf("compression: %s\n", c);
How to read a remote COG over HTTP#
Prefix the URL with /vsicurl/. GDAL reads only the bytes it needs with range requests:
GDALDataset *ds = GDALDataset::Open("/vsicurl/https://example.com/data/cog.tif", GDAL_OF_RASTER);
Tips#
gdalinfo file.tifshows everything GDAL knows about a file;gdalinfo --format GTifflists the creation options of your build.- Call
ds->BuildOverviews("AVERAGE", ...)or rungdaladdoto add reduced-resolution levels for fast display. - Bindings:
osgeo.gdalin Python,org.gdalin Java, godal in Go, gdal in Rust.