Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- {
- "cells": [
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "The aim of this recommended practice is to generate a burn severity map for the assessment of the areas affected by the wildfires using the differenced Normalized Burn Ratio (dNBR), which is the difference of the post-fire NBR and the pre-fire NBR. "
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Import the required packages"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {},
- "outputs": [],
- "source": [
- "import osr\n",
- "import ogr\n",
- "import gdal\n",
- "import numpy as np\n",
- "import matplotlib\n",
- "import matplotlib.pyplot as plt\n",
- "import rasterio\n",
- "import glob\n",
- "from rasterio.plot import show\n",
- "from rasterio.mask import mask\n",
- "from shapely.geometry import mapping\n",
- "import geopandas as gpd\n",
- "import math\n"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Function definitions"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {},
- "outputs": [],
- "source": [
- "def read_band_image(band, path):\n",
- " \"\"\"\n",
- " This function takes as input the Sentinel-2 band name and the path of the \n",
- " folder that the images are stored, reads the image and returns the data as\n",
- " an array\n",
- " input: band string Sentinel-2 band name\n",
- " path string path of the folder\n",
- " output: data array (n x m) array of the band image\n",
- " spatialRef string projection \n",
- " geoTransform tuple affine transformation coefficients\n",
- " targetprj spatial reference\n",
- " \"\"\"\n",
- " a = path+'*B'+band+'*.jp2'\n",
- " img = gdal.Open(glob.glob(a)[0])\n",
- " data = np.array(img.GetRasterBand(1).ReadAsArray())\n",
- " spatialRef = img.GetProjection()\n",
- " geoTransform = img.GetGeoTransform()\n",
- " targetprj = osr.SpatialReference(wkt = img.GetProjection())\n",
- " return data, spatialRef, geoTransform, targetprj\n",
- "\n",
- "def nbr(band1, band2):\n",
- " \"\"\"\n",
- " This function takes an input the arrays of the bands from the read_band_image\n",
- " function and returns the Normalized Burn ratio (NBR)\n",
- " input: band1 array (n x m) array of first band image e.g B8A\n",
- " band2 array (n x m) array of second band image e.g. B12\n",
- " output: nbr array (n x m) normalized burn ratio\n",
- " \"\"\"\n",
- " nbr = (band1 - band2) / (band1 + band2)\n",
- " return nbr\n",
- "\n",
- "def dnbr(nbr1,nbr2):\n",
- " \"\"\"\n",
- " This function takes as input the pre- and post-fire NBR and returns the dNBR\n",
- " input: nbr1 array (n x m) pre-fire NBR\n",
- " nbr2 array (n x m) post-fire NBR\n",
- " output: dnbr array (n x m) dNBR\n",
- " \"\"\"\n",
- " dnbr = nbr1 - nbr2\n",
- " return dnbr\n",
- "\n",
- "def reproject_shp_gdal(infile, outfile, targetprj):\n",
- " \"\"\"\n",
- " This function takes as input the input and output file names and the projection\n",
- " in which the input file will be reprojected and reprojects the input file using\n",
- " gdal\n",
- " input: infile string input filename\n",
- " outfile string output filename\n",
- " targetprj projection (output of function read_band_image)\n",
- " \"\"\"\n",
- " ## reprojection with gdal \n",
- " \n",
- " driver = ogr.GetDriverByName(\"ESRI Shapefile\") \n",
- " dataSource = driver.Open(infile, 1) # 0 means read-only. 1 means writeable.\n",
- " layer = dataSource.GetLayer()\n",
- " sourceprj = layer.GetSpatialRef()\n",
- " transform = osr.CoordinateTransformation(sourceprj, targetprj)\n",
- " \n",
- " # Create the output shapefile\n",
- " outDriver = ogr.GetDriverByName(\"Esri Shapefile\")\n",
- " outDataSource = outDriver.CreateDataSource(outfile)\n",
- " outlayer = outDataSource.CreateLayer('', targetprj, ogr.wkbPolygon)\n",
- " outlayer.CreateField(ogr.FieldDefn('id', ogr.OFTInteger))\n",
- " \n",
- " #Iterate over Features\n",
- " i = 0\n",
- " for feature in layer:\n",
- " transformed = feature.GetGeometryRef()\n",
- " transformed.Transform(transform) #reproject geometry\n",
- "\n",
- " geom = ogr.CreateGeometryFromWkb(transformed.ExportToWkb()) # create geometry from wkb (write geometry of reprojected geometry)\n",
- " defn = outlayer.GetLayerDefn() #layer definition\n",
- " feat = ogr.Feature(defn) #create new feature\n",
- " feat.SetField('id', i) #set id\n",
- " feat.SetGeometry(geom) #set geometry\n",
- " outlayer.CreateFeature(feat) \n",
- " i += 1\n",
- " feat = None\n",
- " \n",
- "def array2raster(array, geoTransform, projection, filename):\n",
- " \"\"\" \n",
- " This function tarnsforms a numpy array to a geotiff projected raster\n",
- " input: array array (n x m) input array\n",
- " geoTransform tuple affine transformation coefficients\n",
- " projection string projection\n",
- " filename string output filename\n",
- " output: dataset gdal raster dataset\n",
- " dataset.GetRasterBand(1) band object of dataset\n",
- " \n",
- " \"\"\"\n",
- " pixels_x = array.shape[1]\n",
- " pixels_y = array.shape[0]\n",
- " \n",
- " driver = gdal.GetDriverByName('GTiff')\n",
- " dataset = driver.Create(\n",
- " filename,\n",
- " pixels_x,\n",
- " pixels_y,\n",
- " 1,\n",
- " gdal.GDT_Float64, )\n",
- " dataset.SetGeoTransform(geoTransform)\n",
- " dataset.SetProjection(projection)\n",
- " dataset.GetRasterBand(1).WriteArray(array)\n",
- " dataset.FlushCache() # Write to disk.\n",
- " return dataset, dataset.GetRasterBand(1) #If you need to return, remenber to return also the dataset because the band don`t live without dataset.\n",
- " \n",
- "def clip_raster(filename, shp):\n",
- " \"\"\"\n",
- " This function clips a raster based on a shapefile\n",
- " input: filename string input raster filename\n",
- " shp dataframe input shapefile open with geopandas\n",
- " output: clipped array (1 x n x m) clipped array \n",
- " clipped_meta dict metadata\n",
- " cr_ext tuple extent of clipped data\n",
- " gt tuple affine transformation coefficients\n",
- " \"\"\"\n",
- " inraster = rasterio.open(filename)\n",
- " \n",
- " extent_geojson = mapping(shp['geometry'][0])\n",
- " clipped, crop_affine = mask(inraster, \n",
- " shapes=[extent_geojson], \n",
- " nodata = np.nan,\n",
- " crop=True)\n",
- " clipped_meta = inraster.meta.copy()\n",
- " # Update the metadata to have the new shape (x and y and affine information)\n",
- " clipped_meta.update({\"driver\": \"GTiff\",\n",
- " \"height\": clipped.shape[0],\n",
- " \"width\": clipped.shape[1],\n",
- " \"transform\": crop_affine})\n",
- " cr_ext = rasterio.transform.array_bounds(clipped_meta['height'], \n",
- " clipped_meta['width'], \n",
- " clipped_meta['transform'])\n",
- " \n",
- " # transform to gdal\n",
- " gt = crop_affine.to_gdal()\n",
- " \n",
- " return clipped, clipped_meta, cr_ext, gt\n",
- " \n",
- "def reclassify(array):\n",
- " \"\"\"\n",
- " This function reclassifies an array\n",
- " input: array array (n x m) input array\n",
- " output: reclass array (n x m) reclassified array\n",
- " \"\"\"\n",
- " reclass = np.zeros((array.shape[0],array.shape[1]))\n",
- " for i in range(0,array.shape[0]):\n",
- " for j in range(0,array.shape[1]):\n",
- " if math.isnan(array[i,j]):\n",
- " reclass[i,j] = np.nan\n",
- " elif array[i,j] < 0.1:\n",
- " reclass[i,j] = 1\n",
- " elif array[i,j] < 0.27:\n",
- " reclass[i,j] = 2\n",
- " elif array[i,j] < 0.44:\n",
- " reclass[i,j] = 3\n",
- " elif array[i,j] < 0.66:\n",
- " reclass[i,j] = 4\n",
- " else:\n",
- " reclass[i,j] = 5\n",
- " \n",
- " return reclass\n",
- " "
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "In the following cell you should have to modify the paths for the images and the shapefile"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {},
- "outputs": [],
- "source": [
- "# Paths\n",
- "path_prefire = \"F:/Burn_Severity/S2A_MSIL2A_20161220T143742_N0204_R096_T18HYF_20161220T145131.SAFE/GRANULE/L2A_T18HYF_A007815_20161220T145131/IMG_DATA/R20m/\"\n",
- "path_postfire = \"F:/Burn_Severity/S2A_MSIL2A_20170218T143751_N0204_R096_T18HYF_20170218T145150.SAFE/GRANULE/L2A_T18HYF_A008673_20170218T145150/IMG_DATA/R20m/\"\n",
- "# Define shapefile\n",
- "infile_shp = \"F:/Burn_Severity/Empedrado_adm_boundary/Empedrado.shp\"\n",
- "# Define reprojected shapefile\n",
- "outfile_shp = \"F:/Burn_Severity/Empedrado_adm_boundary/projected.shp\"\n",
- "# name of the output dNBR raster file\n",
- "filename = \"F:/Burn_Severity/dNBR.tiff\"\n",
- "# name of clipped dNBR raster\n",
- "filename2 = \"F:/Burn_Severity/dNBR_clipped.tiff\"\n",
- "# path to save figure\n",
- "fname = \"F:/Burn_Severity/map.png\"\n",
- "\n",
- "# Sentinel-2 Bands used for NBR calculation \n",
- "band1 = '8A'\n",
- "band2 = '12'\n",
- " \n",
- "# Read the pre-fire band images \n",
- "(pre_fire_b8a, crs, geoTransform, targetprj) = read_band_image(band1, path_prefire)\n",
- "(pre_fire_b12, crs, geoTransform, targetprj) = read_band_image(band2, path_prefire)\n",
- " \n",
- "# Calculation of pre-fire NBR\n",
- "pre_fire_nbr = nbr(pre_fire_b8a.astype(int),pre_fire_b12.astype(int))\n",
- "\n",
- "# Read the post-fire band images\n",
- "(post_fire_b8a, crs, geoTransform, targetprj) = read_band_image(band1, path_postfire)\n",
- "(post_fire_b12, crs, geoTransform, targetprj) = read_band_image(band2, path_postfire)\n",
- " \n",
- "# Calculation of post-fire NBR\n",
- "post_fire_nbr = nbr(post_fire_b8a.astype(int),post_fire_b12.astype(int))\n",
- " \n",
- "# Calculation of dNBR\n",
- "DNBR = dnbr(pre_fire_nbr,post_fire_nbr)\n",
- " \n",
- "# Reprojection of shapefile with gdal to match projection of Sentinel-2 images\n",
- "reproject_shp_gdal(infile_shp, outfile_shp, targetprj)\n",
- " \n",
- "# Read the reprojected shapefile\n",
- "fire_boundary = gpd.read_file(outfile_shp)\n",
- " \n",
- "# project dNBR to images projection\n",
- "dnbr_tif, dnbr_tifBand = array2raster(DNBR, geoTransform, crs, filename)\n",
- " \n",
- "# clip raster dNBR file to Empedrado shapefile\n",
- "(clipped_dnbr, clipped_dnbr_meta, cr_extent, gt) = clip_raster(filename, fire_boundary)\n",
- "clipped_ds , clipped_ds_rasterband = array2raster(clipped_dnbr[0], gt, crs, filename2)\n"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Plot the results"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {},
- "outputs": [],
- "source": [
- "# plot \n",
- "\n",
- "#set colors for plotting and classes\n",
- "cmap = matplotlib.colors.ListedColormap(['green','yellow','orange','red','purple'])\n",
- "cmap.set_over('purple')\n",
- "cmap.set_under('white')\n",
- "bounds = [-0.5, 0.1, 0.27, 0.440, 0.660, 1.3] \n",
- "norm = matplotlib.colors.BoundaryNorm(bounds, cmap.N) \n",
- "\n",
- "fig, ax = plt.subplots(figsize=(10, 10), subplot_kw={'xticks': [], 'yticks': []})\n",
- "cax = ax.imshow(clipped_ds_rasterband.ReadAsArray(), cmap=cmap, norm = norm)\n",
- "plt.title('Burn Severity Map')\n",
- "cbar = fig.colorbar(cax, ax=ax, fraction=0.035, pad=0.04, ticks=[-0.2, 0.18, 0.35, 0.53, 1])\n",
- "cbar.ax.set_yticklabels(['Unburned', 'Low Severity', 'Moderate-low Severity', 'Moderate-high Severity', 'High Severity'])\n",
- "plt.show()\n",
- "plt.savefig(fname, bbox_inches=\"tight\") \n",
- " "
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Calculate burnt area"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {},
- "outputs": [],
- "source": [
- "# calculate burnt area (pixel size 20m*20m)\n",
- "reclass = reclassify(clipped_ds_rasterband.ReadAsArray())\n",
- "k = ['Unburned hectares', 'Low severity hectares', 'Moderate-low severity hectares', 'Moderate-high severity hectares', 'High severity']\n",
- "for i in range(1,6):\n",
- " x = reclass[reclass == i]\n",
- " l= x.size*0.04\n",
- " print(\"%s: %.2f\" % (k[i-1], l))"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {},
- "outputs": [],
- "source": []
- }
- ],
- "metadata": {
- "kernelspec": {
- "display_name": "Python 3",
- "language": "python",
- "name": "python3"
- },
- "language_info": {
- "codemirror_mode": {
- "name": "ipython",
- "version": 3
- },
- "file_extension": ".py",
- "mimetype": "text/x-python",
- "name": "python",
- "nbconvert_exporter": "python",
- "pygments_lexer": "ipython3",
- "version": "3.6.5"
- }
- },
- "nbformat": 4,
- "nbformat_minor": 2
- }
Add Comment
Please, Sign In to add comment