*focus on raster functionality
Not a Geographic Information System!
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.
conda
(Tutorial: Getting Started with conda)
(the %matplotlib inline
is a jupyter notebook command to show matplotlib plots in the output)
%matplotlib inline
import os
import sys
import numpy
import matplotlib.pyplot as plt
from osgeo import gdal
# 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'>
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
The gdal.Open() function creates a GDAL Dataset object based on the given file.
# 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
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
# 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
# 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'}
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
CreateCopy() or Translate() functions in python, gdal_translate in the command-line.
# Check contents of Landsat 8 processed data folder.
print(os.listdir(LC08proc_path))
[]
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.
print(os.listdir(LC08proc_path))
['LC08_B2.dat', 'LC08_B2.dat.aux.xml', 'LC08_B2.hdr', 'LC08_B2.vrt']
Get Sentinel-2 bands in a sorted order accessible via key-value pairs.
# 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
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)
# Plot image in Matplotlib.
plt.figure(figsize=(10, 10))
plt.imshow(RGB_Image, extent=(ulx, lrx, lry, uly))
<matplotlib.image.AxesImage at 0x7c031e0470>
# Optional -- Save figure.
# plt.savefig('TestImage.png', format='png', dpi=300, bbox_inches='tight')
# Clean Up.
plt.close()
img = None
RGB_Image = None
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
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)
plt.figure(figsize=(10, 10))
plt.imshow(RGB_Image_crop, extent=(ulx, lrx, lry, uly))
<matplotlib.image.AxesImage at 0x7c039e7cf8>
# Optional -- Save figure.
# plt.savefig('TestImage.png', format='png', dpi=300, bbox_inches='tight')
# Clean Up.
plt.close()
img = None
RGB_Image_crop = None
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.
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
# 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)
0
desired_bands = ['B03', 'B04', 'B08']
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.
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)
plt.figure(figsize=(10, 10))
plt.imshow(NirRG_Image, extent=(ulx, lrx, lry, uly))
<matplotlib.image.AxesImage at 0x7c04842358>
# 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
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
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
# 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)
plt.figure(figsize=(10, 10))
plt.imshow(NirRG_Image_crop, extent=(ulx, lrx, lry, uly))
<matplotlib.image.AxesImage at 0x7c048c7ba8>
# 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
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
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
E-Mail: hannah.augustin@sbg.ac.at
GitHub: @augustinh22
Website: http://hannahaugustin.at