GDAL

Geospatial Data Abstraction Library

a brief introduction

Overview*


  • about + purpose
  • functionality)
  • installation + basic use
  • resources + tutorials
GDAL logo

*focus on raster functionality

About + Purpose

Very Brief History

  • since 1998 --- intitial release in 2000 (Frank Warmerdam)
  • taken over by the Open Source Geospatial Foundation after v1.3.2 (ca. 2006)
  • previously separate from OGR Simple Features Library, now more tightly coupled
    • OGR refers to the vector data model and functionality
    • often referred to as GDAL/OGR
  • currently v2.3.2 (September 2018)

About

  • X/MIT License (permissive free software license)
  • C/C++ based, bindings for python, R, Java, Perl, C#, Ruby …
  • cross-platform geospatial library (MacOS, Linux/Unix, Windows, Android, iOS)

(Intended) Purpose

  • library to read/write raster and vector geospatial formats
    • more than 200 supported geospatial data formats
  • presents a single abstract data model for both raster and vector data formats, respectively
  • provides various command-line utilities for translation and geospatial processing

Not a Geographic Information System!

Some Supported Raster Data Formats (of over 155)


  • TerraSAR-X Complex SAR Data Product
  • ENVI .hdr Labelled Raster
  • GeoPackage
  • GRASS Raster Format
  • GeoTiff
  • JPEG2000
  • GDAL VRT
  • Geospatial PDF
  • Rasdaman
  • Sentinel 1 SAFE
  • Sentinel 2
  • ArcSDE Raster

GDAL Command-line List Drivers:

gdal_translate --formats

Some Software Utilising GDAL

gdal_software_logos.png

Functionality

Some GDAL Utilities

gdalinfo - Report information about a file.

gdal_translate - Copy a raster file, with control of output format.

gdalwarp - Warp an image into a new coordinate system.

gdaldem - Tools to analyze and visualize DEMs.

gdal_merge - Build a quick mosaic from a set of images.

gdal_rasterize - Rasterize vectors into raster file.

gdal_polygonize - Generate polygons from raster.

gdal_pansharpen - Perform a pansharpen operation.

gdalcompare - Compare two images and report on differences.

GDAL Utilities docs here.

QGIS_Transform.png

gdalwarp -overwrite -t_srs EPSG:4326 -dstnodata 0 -of GTiff inputdata.jpg outputdata.tif

gdalwarp docs here.

Installation

Command-line GDAL Utilities

MacOS: via Homebrew or other binary distributions (e.g. kingchaos)

Linux/Unix: access through various standard linux repositories

Windows: OSGeo4W recommended

Note: If QGIS is installed, GDAL is already installed somewhere...

Python Bindings

  • Recommended: the python virtual environment, package and dependency manager conda
    1. install Anaconda or miniconda (simplified version)
    2. start and check installation
    3. manage environments including different versions of python
    4. search and find different versions of packages
    5. create an environment with GDAL and whatever else you require
    6. activate the environment
    7. Go!

(Tutorial: Getting Started with conda)

Basic Use

Set-up necessary python imports

(the %matplotlib inline is a jupyter notebook command to show matplotlib plots in the output)

In [1]:
%matplotlib inline

import os
import sys

import numpy
import matplotlib.pyplot as plt
from osgeo import gdal

Check Versions

In [2]:
# Check version of python:
print(sys.version)

# We can check which version we're running by printing the "__version__" variable
print("GDAL's version is: " + gdal.__version__)

# This shows where the version of GDAL is located (in this case, in a conda environment)
print(gdal)

# This allows GDAL to throw Python Exceptions
gdal.UseExceptions()
3.7.1 | packaged by conda-forge | (default, Nov 13 2018, 19:01:41) [MSC v.1900 64 bit (AMD64)]
GDAL's version is: 2.3.2
<module 'osgeo.gdal' from 'C:\\Users\\b1041827\\AppData\\Local\\Continuum\\miniconda2\\envs\\jupyter_slides\\lib\\site-packages\\osgeo\\gdal.py'>

Command-line Equivalent:

python --version

gdalinfo --version

Get Info about a dataset

The documentation on the Info() function can be found here and for the InfoOptions() fucntion here.

Landsat 8 band

Sentinel-2: Granule 37SBA, 2 November 2018

In [4]:
info_options = gdal.InfoOptions(computeMinMax=True, stats=True)

print(gdal.Info(LC08band2_path, options=info_options))
Driver: GTiff/GeoTIFF
Files: C:\Users\b1041827\ownCloud\01_work\02_presentations\jupyter_slides\gdal_intro\data\LC08_L1TP_146035_20170201_20170215_01_T1\LC08_L1TP_146035_20170201_20170215_01_T1_B2.TIF
       C:\Users\b1041827\ownCloud\01_work\02_presentations\jupyter_slides\gdal_intro\data\LC08_L1TP_146035_20170201_20170215_01_T1\LC08_L1TP_146035_20170201_20170215_01_T1_MTL.txt
Size is 7761, 7891
Coordinate System is:
PROJCS["WGS 84 / UTM zone 44N",
    GEOGCS["WGS 84",
        DATUM["WGS_1984",
            SPHEROID["WGS 84",6378137,298.257223563,
                AUTHORITY["EPSG","7030"]],
            AUTHORITY["EPSG","6326"]],
        PRIMEM["Greenwich",0,
            AUTHORITY["EPSG","8901"]],
        UNIT["degree",0.0174532925199433,
            AUTHORITY["EPSG","9122"]],
        AUTHORITY["EPSG","4326"]],
    PROJECTION["Transverse_Mercator"],
    PARAMETER["latitude_of_origin",0],
    PARAMETER["central_meridian",81],
    PARAMETER["scale_factor",0.9996],
    PARAMETER["false_easting",500000],
    PARAMETER["false_northing",0],
    UNIT["metre",1,
        AUTHORITY["EPSG","9001"]],
    AXIS["Easting",EAST],
    AXIS["Northing",NORTH],
    AUTHORITY["EPSG","32644"]]
Origin = (281385.000000000000000,4107315.000000000000000)
Pixel Size = (30.000000000000000,-30.000000000000000)
Metadata:
  AREA_OR_POINT=Point
  METADATATYPE=ODL
Image Structure Metadata:
  INTERLEAVE=BAND
Corner Coordinates:
Upper Left  (  281385.000, 4107315.000) ( 78d32'25.46"E, 37d 5'11.93"N)
Lower Left  (  281385.000, 3870585.000) ( 78d36'21.01"E, 34d57'15.22"N)
Upper Right (  514215.000, 4107315.000) ( 81d 9'35.99"E, 37d 6'43.40"N)
Lower Right (  514215.000, 3870585.000) ( 81d 9'20.65"E, 34d58'39.84"N)
Center      (  397800.000, 3988950.000) ( 79d51'55.82"E, 36d 2'23.01"N)
Band 1 Block=7761x1 Type=UInt16, ColorInterp=Gray
    Computed Min/Max=0.000,50699.000
  Minimum=0.000, Maximum=50699.000, Mean=7705.460, StdDev=6648.513
  Metadata:
    STATISTICS_MAXIMUM=50699
    STATISTICS_MEAN=7705.4601707249
    STATISTICS_MINIMUM=0
    STATISTICS_STDDEV=6648.5125029085

Command-line Equivalent:

gdalinfo path_to_file.tif -mm -stats

gdalinfo docs here.

Open and Close a Dataset in Python

The gdal.Open() function creates a GDAL Dataset object based on the given file.

In [5]:
# Open the Landsat 8 Band 2 file as a GDAL Dataset object.
in_ds = gdal.Open(LC08band2_path, gdal.GA_ReadOnly)

# Close GDAL Dataset object.
in_ds = None

Access Metadata from Dataset Object in Python

In [6]:
in_ds = gdal.Open(LC08band2_path, gdal.GA_ReadOnly)

# Find the number of bands in the dataset object.
print("[ NUMBER OF BANDS ] = ", in_ds.RasterCount)
[ NUMBER OF BANDS ] =  1
In [7]:
# Create a GDAL raster band object from the only band in the dataset object.
in_band = in_ds.GetRasterBand(1)

print("[ BAND DATA TYPE ] = ", gdal.GetDataTypeName(in_band.DataType))
print("[ NO DATA VALUE ] = ", in_band.GetNoDataValue())
[ BAND DATA TYPE ] =  UInt16
[ NO DATA VALUE ] =  None
In [9]:
# Compute statistics if needed.
if in_band.GetMinimum() is None or in_band.GetMaximum()is None:
    in_band.ComputeStatistics(0)
    print("Statistics computed.")

print("[ OVERALL STATS ] = ", in_band.GetMetadata())
[ OVERALL STATS ] =  {'STATISTICS_MAXIMUM': '50699', 'STATISTICS_MEAN': '7705.4601707249', 'STATISTICS_MINIMUM': '0', 'STATISTICS_STDDEV': '6648.5125029085'}
In [10]:
print("[ MIN ] = ", in_band.GetMinimum())
print("[ MAX ] = ", in_band.GetMaximum())
print("[ SCALE ] = ", in_band.GetScale())

# Close everything.
in_ds = None
in_band = None
[ MIN ] =  0.0
[ MAX ] =  50699.0
[ SCALE ] =  1.0

Create Copies in Different Formats

CreateCopy() or Translate() functions in python, gdal_translate in the command-line.

In [11]:
# Check contents of Landsat 8 processed data folder.
print(os.listdir(LC08proc_path))
[]
In [12]:
in_ds = gdal.Open(LC08band2_path, gdal.GA_ReadOnly)

# Define path to save copy and with what driver.
dst_filename = os.path.join(LC08proc_path, "LC08_B2.vrt")
driver = gdal.GetDriverByName("VRT")
dst_ds = driver.CreateCopy(dst_filename, in_ds, strict=0)
print("VRT copy of Band 2 has been created.")

dst_filename = os.path.join(LC08proc_path, "LC08_B2.dat")
driver = gdal.GetDriverByName("ENVI")
dst_ds = driver.CreateCopy(dst_filename, in_ds, strict=0)
print("ENVI copy of Band 2 has been created.")

# Close initial and resulting datasets.
dst_ds = None
in_ds = None
VRT copy of Band 2 has been created.
ENVI copy of Band 2 has been created.
In [13]:
print(os.listdir(LC08proc_path))
['LC08_B2.dat', 'LC08_B2.dat.aux.xml', 'LC08_B2.hdr', 'LC08_B2.vrt']

Command-line Equivalent:

gdal_translate -of "VRT" path_to_input.tif LC08_B2.vrt

gdal_translate -of "ENVI" path_to_input.tif LC08_B2.dat

gdal_translate docs here.

Access Sentinel-2 bands as dictionary.

Get Sentinel-2 bands in a sorted order accessible via key-value pairs.

In [14]:
# Create dictionary with key-value pairs for easier access to bands.
S2_bands = {}

for entry in os.listdir(S2_path):
    if (entry.endswith('.jp2')):
            # This makes bands accessible by name (e.g. B01, B8A).
            band_name = entry[-7:-4]
            S2_bands[band_name]=os.path.join(S2_path, entry)

# Using the dictionary, individual bands can be accessed so:
print("Band 8A is called: " + os.path.basename(S2_bands['B8A']))
del band_name
Band 8A is called: T37SBA_20181102T082101_B8A.jp2

Open True Colour Band and view with Matplotlib

In [15]:
img = gdal.Open(S2_bands['TCI'], gdal.GA_ReadOnly)

# Read GDAL Dataset Object as a Numpy Array
RGB_Image = img.ReadAsArray()
print(RGB_Image.shape)

# Get extent and boundary
ulx, xres, xskew, uly, yskew, yres  = img.GetGeoTransform()
lrx = ulx + (img.RasterXSize * xres)
lry = uly + (img.RasterYSize * yres)
print(ulx, lrx, lry, uly)

# Re-order dimensions for matplotlib.
RGB_Image = numpy.dstack(((RGB_Image[0]), (RGB_Image[1]), (RGB_Image[2]))).astype('uint8')
print(RGB_Image.shape)
(3, 10980, 10980)
199980.0 309780.0 3990240.0 4100040.0
(10980, 10980, 3)
In [16]:
# Plot image in Matplotlib.
plt.figure(figsize=(10, 10))
plt.imshow(RGB_Image, extent=(ulx, lrx, lry, uly))
Out[16]:
<matplotlib.image.AxesImage at 0x7c031e0470>
In [17]:
# Optional -- Save figure.
# plt.savefig('TestImage.png', format='png', dpi=300, bbox_inches='tight')

# Clean Up.
plt.close()
img = None
RGB_Image = None

Crop True Colour Image

In [18]:
img = gdal.Open(S2_bands['TCI'], gdal.GA_ReadOnly)

# Define cropped area in same CRS as image (EPSG:32637)
ulx_c = 248200
lrx_c = 269800
lry_c = 4014100
uly_c = 4035700

cropped_path = os.path.join(S2proc_path, "cropped_TCI.tif")

translate_options = gdal.TranslateOptions(projWin=[ulx_c, uly_c, lrx_c, lry_c], stats=True)
translated_stack = gdal.Translate(cropped_path, img, options=translate_options)

img = None
in_crs = None
translated_stack = None

Command-line Equivalent:

gdal_translate -projwin 248200 4035700 269800 4014100 -stats path_to_input.tif cropped_TCI.tif

gdal_translate docs here.

Display Cropped TCI Image using Matplotlib

In [19]:
img = gdal.Open(cropped_path, gdal.GA_ReadOnly)
RGB_Image_crop = img.ReadAsArray()
print(RGB_Image_crop.shape)

# Get extent and boundary
ulx, xres, xskew, uly, yskew, yres  = img.GetGeoTransform()
lrx = ulx + (img.RasterXSize * xres)
lry = uly + (img.RasterYSize * yres)
print(ulx, lrx, lry, uly)

# Re-order dimensions for matplotlib.
RGB_Image_crop = numpy.dstack(((RGB_Image_crop[2]), (RGB_Image_crop[1]), (RGB_Image_crop[0]))).astype('uint8')
print(RGB_Image_crop.shape)
(3, 2160, 2160)
248200.0 269800.0 4014100.0 4035700.0
(2160, 2160, 3)
In [20]:
plt.figure(figsize=(10, 10))
plt.imshow(RGB_Image_crop, extent=(ulx, lrx, lry, uly))
Out[20]:
<matplotlib.image.AxesImage at 0x7c039e7cf8>
In [21]:
# Optional -- Save figure.
# plt.savefig('TestImage.png', format='png', dpi=300, bbox_inches='tight')

# Clean Up.
plt.close()
img = None
RGB_Image_crop = None

Create Empty output dataset object with GeoTiff driver

This creates an empty dataset object intended for a 3 band GeoTIFF stack. It uses Sentinel-2's band 2 as a template to retrieve projection and pixel information to base the empty dataset object on.

In [22]:
img = gdal.Open(S2_bands['B02'], gdal.GA_ReadOnly)

# Get raster georeference and projection info from B02.
projection = img.GetProjection()

transform = img.GetGeoTransform()
print("xOrigin: " + str(transform[0]))
print("yOrigin: " + str(transform[3]))
print("pixelWidth: " + str(transform[1]))
print("pixelHeight: " + str(transform[5]))

img_rows = img.RasterYSize
img_cols = img.RasterXSize
print("Rows: " + str(img_rows))
print("Columns: " + str(img_cols))

img = None
xOrigin: 199980.0
yOrigin: 4100040.0
pixelWidth: 10.0
pixelHeight: -10.0
Rows: 10980
Columns: 10980
In [23]:
# Get GeoTIFF driver and define output filename and path.
gdal_format = 'GTiff'
driver = gdal.GetDriverByName(gdal_format)

stacked_file = "B348_stack.tif"
stacked_path = os.path.join(S2proc_path, stacked_file)

# Create output dataset object with the right size, 3 bands and as an 8-bit unsigned GeoTiff (driver).
outDs = driver.Create(stacked_path, img_cols, img_rows, 3, gdal.GDT_Byte)

# Georeference the outpute dataset object and set the projection based on B02.
outDs.SetGeoTransform(transform)
outDs.SetProjection(projection)
Out[23]:
0

Create false colour image in empty Dataset object

In [24]:
desired_bands = ['B03', 'B04', 'B08']

Sentinel2_Spectral_Bands.jpg

In [25]:
for idx, val in enumerate(desired_bands):

    # Open the file.
    img = gdal.Open(S2_bands[val], gdal.GA_ReadOnly)

    # Read the one band as an array with the proper size.
    img_band = img.GetRasterBand(1)
    img_rows = img.RasterYSize
    img_cols = img.RasterXSize
    img_array = img_band.ReadAsArray(0, 0, img_cols, img_rows)

    # Adjust outliers using numpy (very high reflectance and negative).
    outData = img_array / 10000.0
    outData = numpy.where((outData > 1), (1), outData)
    outData = numpy.where((outData < 0), (0), outData)
    img_array = None

    # Convert to 8-bit.
    outData = ((numpy.absolute(outData) * 255.0) + 0.5).astype(int)
    
    # Write the array to the proper raster band in the output dataset
    # object and flush to disk
    outBand = outDs.GetRasterBand(idx+1)
    outBand.WriteArray(outData, 0, 0)
    outBand.FlushCache()
    
    # Calculate statistics and set to band.
    stats = outBand.ComputeStatistics(False)
    outBand.SetStatistics(stats[0], stats[1], stats[2], stats[3])
    print("Added " + val + " as band " + str(idx+1) + " to stack.")
    
    # Clean up band specific variables.
    img_band = None
    band_id = None
    stats = None
    img = None
    del outData
    del outBand

outDs = None
Added B03 as band 1 to stack.
Added B04 as band 2 to stack.
Added B08 as band 3 to stack.

Display False Colour (Nir, R, G) Image using Matplotlib

In [27]:
img = gdal.Open(stacked_path, gdal.GA_ReadOnly)
GRNir_Image = img.ReadAsArray()
print(GRNir_Image.shape)

# Get extent and boundary
ulx, xres, xskew, uly, yskew, yres  = img.GetGeoTransform()
lrx = ulx + (img.RasterXSize * xres)
lry = uly + (img.RasterYSize * yres)
print(ulx, lrx, lry, uly)

# Re-order dimensions for matplotlib.
NirRG_Image = numpy.dstack(((GRNir_Image[2]), (GRNir_Image[1]), (GRNir_Image[0]))).astype('uint8')
print(NirRG_Image.shape)
(3, 10980, 10980)
199980.0 309780.0 3990240.0 4100040.0
(10980, 10980, 3)
In [28]:
plt.figure(figsize=(10, 10))
plt.imshow(NirRG_Image, extent=(ulx, lrx, lry, uly))
Out[28]:
<matplotlib.image.AxesImage at 0x7c04842358>
In [29]:
# Optional -- Save figure.
# plt.savefig('TestImage.png', format='png', dpi=300, bbox_inches='tight')

# Clean Up.
plt.close()
img = None
GRNir_Image = None
NirRG_Image = None

Crop False Colour Image

In [30]:
img = gdal.Open(stacked_path, gdal.GA_ReadOnly)

# Define cropped area in same CRS as image (EPSG:32637)
ulx_c = 248200
lrx_c = 269800
lry_c = 4014100
uly_c = 4035700

cropped_path = os.path.join(S2proc_path, "false_crop.tif")

translate_options = gdal.TranslateOptions(projWin=[ulx_c, uly_c, lrx_c, lry_c], stats=True)
translated_stack = gdal.Translate(cropped_path, img, options=translate_options)

img = None
in_crs = None
translated_stack = None
In [31]:
print(gdal.Info(cropped_path))
Driver: GTiff/GeoTIFF
Files: C:\Users\b1041827\ownCloud\01_work\02_presentations\jupyter_slides\gdal_intro\data\S2A_MSIL1C_20181102T082101_N0206_R121_T37SBA_20181102T094559.SAFE\GRANULE\L1C_T37SBA_A017564_20181102T082102\PROC_DATA\false_crop.tif
Size is 2160, 2160
Coordinate System is:
PROJCS["WGS 84 / UTM zone 37N",
    GEOGCS["WGS 84",
        DATUM["WGS_1984",
            SPHEROID["WGS 84",6378137,298.257223563,
                AUTHORITY["EPSG","7030"]],
            AUTHORITY["EPSG","6326"]],
        PRIMEM["Greenwich",0,
            AUTHORITY["EPSG","8901"]],
        UNIT["degree",0.0174532925199433,
            AUTHORITY["EPSG","9122"]],
        AUTHORITY["EPSG","4326"]],
    PROJECTION["Transverse_Mercator"],
    PARAMETER["latitude_of_origin",0],
    PARAMETER["central_meridian",39],
    PARAMETER["scale_factor",0.9996],
    PARAMETER["false_easting",500000],
    PARAMETER["false_northing",0],
    UNIT["metre",1,
        AUTHORITY["EPSG","9001"]],
    AXIS["Easting",EAST],
    AXIS["Northing",NORTH],
    AUTHORITY["EPSG","32637"]]
Origin = (248200.000000000000000,4035700.000000000000000)
Pixel Size = (10.000000000000000,-10.000000000000000)
Metadata:
  AREA_OR_POINT=Area
Image Structure Metadata:
  INTERLEAVE=PIXEL
Corner Coordinates:
Upper Left  (  248200.000, 4035700.000) ( 36d11'27.83"E, 36d26' 0.63"N)
Lower Left  (  248200.000, 4014100.000) ( 36d11'52.95"E, 36d14'20.43"N)
Upper Right (  269800.000, 4035700.000) ( 36d25'54.45"E, 36d26'20.16"N)
Lower Right (  269800.000, 4014100.000) ( 36d26'17.43"E, 36d14'39.83"N)
Center      (  259000.000, 4024900.000) ( 36d18'53.16"E, 36d20'20.48"N)
Band 1 Block=2160x1 Type=Byte, ColorInterp=Red
  Min=12.000 Max=255.000 
  Minimum=12.000, Maximum=255.000, Mean=29.866, StdDev=6.453
  Metadata:
    STATISTICS_MAXIMUM=255
    STATISTICS_MEAN=29.866254286694
    STATISTICS_MINIMUM=12
    STATISTICS_STDDEV=6.453025266892
Band 2 Block=2160x1 Type=Byte, ColorInterp=Green
  Min=7.000 Max=151.000 
  Minimum=7.000, Maximum=151.000, Mean=31.448, StdDev=9.213
  Metadata:
    STATISTICS_MAXIMUM=151
    STATISTICS_MEAN=31.448222093621
    STATISTICS_MINIMUM=7
    STATISTICS_STDDEV=9.2125527750705
Band 3 Block=2160x1 Type=Byte, ColorInterp=Blue
  Min=8.000 Max=255.000 
  Minimum=8.000, Maximum=255.000, Mean=44.951, StdDev=12.316
  Metadata:
    STATISTICS_MAXIMUM=255
    STATISTICS_MEAN=44.950881344307
    STATISTICS_MINIMUM=8
    STATISTICS_STDDEV=12.316015306077

Command-line Equivalent:

gdal_translate -projwin 248200 4035700 269800 4014100 -stats path_to_input.tif false_crop.tif

gdal_translate docs here.

Display Cropped False Colour (Nir, R, G) Image using Matplotlib

In [32]:
# Open the file.
img = gdal.Open(cropped_path, gdal.GA_ReadOnly)
GRNir_Image_crop = img.ReadAsArray()
print(GRNir_Image_crop.shape)

# Get extent and boundary
ulx, xres, xskew, uly, yskew, yres  = img.GetGeoTransform()
lrx = ulx + (img.RasterXSize * xres)
lry = uly + (img.RasterYSize * yres)
print(ulx, lrx, lry, uly)

# Re-order dimensions for matplotlib.
NirRG_Image_crop = numpy.dstack(((GRNir_Image_crop[2]), (GRNir_Image_crop[1]), (GRNir_Image_crop[0]))).astype('uint8')
print(NirRG_Image_crop.shape)
(3, 2160, 2160)
248200.0 269800.0 4014100.0 4035700.0
(2160, 2160, 3)
In [33]:
plt.figure(figsize=(10, 10))
plt.imshow(NirRG_Image_crop, extent=(ulx, lrx, lry, uly))
Out[33]:
<matplotlib.image.AxesImage at 0x7c048c7ba8>
In [34]:
# Optional -- Save figure.
# plt.savefig('TestImage.png', format='png', dpi=300, bbox_inches='tight')

# Clean Up.
plt.close()
img = None
GRNir_Image_crop = None
NirRG_Image_crop = None

Further Tutorials and Resources

Official GDAL API Tutorial for C++, C and Python

Medium: A Gentle Introduction to GDAL (Parts 1, 2, 3, 4)

Planet Lab: Getting Started with GDAL

University of Edinburgh: Simple Raster Conversion with GDAL

Digital Geography: Reading and Writing Geospatial files using GDAL and python

GeoNode: Introduction and Advanced Processing with GDAL Utilities

Documentation for all GDAL Utilities

GDAL Cheat Sheet by @dwtkns on GitHub

Python GDAL/OGR Cookbook: Raster Layers

Jupyter notebook on Exploring the GDALDataset Class by @ceholden on GitHub

Creating Natural Color RGB Composites from Landsat 8 data in Python

Basic GDAL Commands for Cartographers by Christopher Wesson

Other Python Image Libraries

Rasterio: essentially wraps GDAL functionality

rasterstats: allows zonal statistics from raster files or numpy arrays using geometries

scikit-image: for image processing in python

Pillow: Python Imaging Library fork for image processing

scipy.ndimage: functions for multi-dimensional image processing

Thank you for your attention!

E-Mail: hannah.augustin@sbg.ac.at

GitHub: @augustinh22

Website: http://hannahaugustin.at

Slides: http://slides.hannahaugustin.at/maptime/GDAL_intro/