from PIL import Image import requests from sat7_pointer import * import numpy as np from tqdm import tqdm from lxml import etree # Load Landsat 7 band 1, 2, 3 TIF images and create a composite image # from the three bands. # # The composite image is a greyscale image with a red, green and blue # channel. # # The red channel is the red channel of band 3, the green channel is the # green channel of band 2 and the blue channel is the blue channel of band 1. # # The composite image is saved as a PNG file. band1 = Image.open("LE07_L1TP_177025_20210723_20210818_02_T1_B1.TIF") band2 = Image.open("LE07_L1TP_177025_20210723_20210818_02_T1_B2.TIF") band3 = Image.open("LE07_L1TP_177025_20210723_20210818_02_T1_B3.TIF") composite = Image.merge("RGB", (band3, band2, band1)) # Load corner coordinates of the image. # # The coordinates are stored in a MTL text file. mtl_data = load_metadata("LE07_L1TP_177025_20210723_20210818_02_T1_MTL.txt") # Fetch coordinates of a city. # # The coordinates of a city are fetched from the OpenStreetMap API. # # The coordinates are used to calculate the corner coordinates of the # composite image. # # # City is Belgorod, Russia. url = "http://nominatim.openstreetmap.org/search?q=Belgorod,+Russia&format=json" response = requests.get(url) data = response.json() # Convert bounding box coordinates to image coordinates. x0, y1 = lat_lot_to_pixel( data[0]["boundingbox"][0], data[0]["boundingbox"][2], mtl_data ) x1, y0 = lat_lot_to_pixel( data[0]["boundingbox"][1], data[0]["boundingbox"][3], mtl_data ) print(x0, y0) print(x1, y1) print(composite.size) # Crop the image to the bounding box coordinates. cropped = composite.crop((x0, y0, x1, y1)) cropped.save("cropped.png") ############################################################################### # LAB 2 ############################################################################### # Load landsat band 4 TIF image. # Band 4 is near infrared. band4 = Image.open("LE07_L1TP_177025_20210723_20210818_02_T1_B4.TIF") # Claculate the NDVI. # # The NDVI is calculated by dividing # difference of the red and near infrared band 4 by the sum of the # near infrared and near infrared band 4. # # The NDVI is calculated for each pixel in the cropped image. # # The NDVI is saved as a PNG image. ndvi = band4.copy().crop((x0, y0, x1, y1)) red = band3.copy().crop((x0, y0, x1, y1)) result = cropped.copy() print(red.getpixel((0, 0))) print(ndvi.getpixel((0, 0))) # Colors for the NDVI. def get_color(value): if value < 0.033: return (255, 255, 255) elif value < 0.066: return (196, 184, 168) elif value < 0.1: return (180, 150, 108) elif value < 0.133: return (164, 130, 76) elif value < 0.166: return (148, 114, 60) elif value < 0.2: return (124, 158, 44) elif value < 0.25: return (148, 182, 20) elif value < 0.3: return (116, 170, 4) elif value < 0.35: return (100, 162, 4) elif value < 0.4: return (84, 150, 4) elif value < 0.45: return (60, 134, 4) elif value < 0.5: return (28, 114, 4) elif value < 0.6: return (4, 96, 4) elif value < 0.7: return (4, 74, 4) elif value < 0.8: return (4, 56, 4) elif value < 0.9: return (4, 40, 4) else: return (0, 0, 0) for x in tqdm(range(ndvi.size[0]), desc="NDVI"): for y in range(ndvi.size[1]): r = red.getpixel((x, y)) nir = ndvi.getpixel((x, y)) if nir + r == 0: result.putpixel((x, y), get_color(0)) else: result.putpixel((x, y), get_color((nir - r) / (nir + r))) result.save("ndvi.png") # Calculate FAPAR (Fraction of Absorbed Photosynthetically Active Radiation) # for each pixel in the cropped image. # # Bands 1, 3, 4 are used. solar_zenith_angle = np.radians(float(mtl_data["SUN_ELEVATION"])) sensor_zenith_angle = np.radians(0) sun_sensor_relative_azimuth = np.radians(float(mtl_data["SUN_AZIMUTH"])) gain = [float(mtl_data["RADIANCE_MULT_BAND_" + str(i)]) for i in [1, 3, 4]] offset = [float(mtl_data["RADIANCE_ADD_BAND_" + str(i)]) for i in [1, 3, 4]] dsol = float(mtl_data["EARTH_SUN_DISTANCE"]) pic = [0.643, 0.80760, 0.89472] k = [0.76611, 0.63931, 0.81037] theta = [-0.10055, -0.06156, -0.03924] k = [0.63931,0.81037, 0.76611] pic = [0.80760, 0.89472, 0.643] theta = [-0.06156, -0.03924, -0.10055] E0 = [1969, 1551, 1044] cosg = np.cos(solar_zenith_angle) * np.cos(sensor_zenith_angle) + np.sin( solar_zenith_angle ) * np.sin(sensor_zenith_angle) * np.cos(sun_sensor_relative_azimuth) G = ( np.tan(solar_zenith_angle) ** 2 + np.tan(sensor_zenith_angle) ** 2 - 2 * np.tan(solar_zenith_angle) * np.tan(sensor_zenith_angle) * np.cos(sun_sensor_relative_azimuth) ) ** 0.5 polynoms = np.array( [ [0.27505, 0.35511, -0.004, -0.322, 0.299, -0.0131, 0, 0, 0, 0, 0], [-10.036, -0.019804, 0.55438, 0.14108, 12.494, 0, 0, 0, 0, 0, 1], [ 0.42720, 0.069884, -0.33771, 0.24690, -1.0821, -0.30401, -1.1024, -1.2596, -0.31949, -1.4864, 0, ], ] ) blue = band1.copy().crop((x0, y0, x1, y1)) red = band3.copy().crop((x0, y0, x1, y1)) nir = band4.copy().crop((x0, y0, x1, y1)) result = cropped.copy() f1 = [ ((np.cos(solar_zenith_angle) * np.cos(sensor_zenith_angle)) ** (k[i] - 1)) / (np.cos(solar_zenith_angle) + np.cos(sensor_zenith_angle)) ** (1 - k[i]) for i in range(3) ] f2 = [ (1 - theta[i] ** 2) / (1 + 2 * theta[i] * cosg + theta[i] ** 2) ** (3 / 2) for i in range(3) ] f3 = [1 + (1 - pic[i]) / (1 + G) for i in range(3)] F = [f1[i] * f2[i] * f3[i] for i in range(3)] def get_color_fapar(value, rho): if (0 < rho[0] and rho[0] < 0.257752) \ and (0 < rho[1] and rho[1] < 0.48407) \ and (0 < rho[2] and rho[2] < 0.683928) \ and (rho[0] <= rho[2]) \ and (rho[2] >= 1.26826*rho[1]): return get_color(value) if (rho[0] <= 0) or (rho[1] <= 0) or (rho[2] <= 0): return (0, 0, 0) if (rho[0] >= 0.257752) or (rho[1] >= 0.48407) or (rho[2] >= 0.683928): return (255, 255, 255) if (0 < rho[0] and rho[0] < 0.257752) \ and (0 < rho[1] and rho[1] < 0.48407) \ and (0 < rho[2] and rho[2] < 0.683928) \ and (rho[0] >= rho[2]): return (0, 0, 255) if (0 < rho[0] and rho[0] < 0.257752) \ and (0 < rho[1] and rho[1] < 0.48407) \ and (0 < rho[2] and rho[2] < 0.683928) \ and (rho[0] <= rho[2]) \ and (1.25*rho[1] > rho[2]): return (255, 150, 150) if (rho[1] < 0) or (rho[2] < 0): return (0, 0, 0) if value < 0 or value > 1: return (0, 0, 0) return (int(180.0 * (1 - value)), 255, 255) for x in tqdm(range(result.size[0]), desc="FAPAR"): for y in range(result.size[1]): bands = [blue.getpixel((x, y)), red.getpixel((x, y)), nir.getpixel((x, y))] rho_i = [ ( (np.pi * (gain[i] * bands[i] + offset[i]) * dsol ** 2) / (E0[i] * np.cos(sensor_zenith_angle)) ) / F[i] for i in range(3) ] g1 = ( (polynoms[1, 0] * (rho_i[0] + polynoms[1, 1]) ** 2) + (polynoms[1, 2] * (rho_i[1] + polynoms[1, 3]) ** 2) + polynoms[1, 4] * rho_i[0] * rho_i[1] ) / ( polynoms[1, 5] * (rho_i[0] + polynoms[1, 6]) ** 2 + polynoms[1, 7] * (rho_i[1] + polynoms[1, 8]) ** 2 + polynoms[1, 9] * rho_i[0] * rho_i[1] + polynoms[1, 10] ) g2 = ( (polynoms[2, 0] * (rho_i[0] + polynoms[2, 1]) ** 2) + (polynoms[2, 2] * (rho_i[2] + polynoms[2, 3]) ** 2) + polynoms[2, 4] * rho_i[0] * rho_i[2] ) / ( polynoms[2, 5] * (rho_i[0] + polynoms[2, 6]) ** 2 + polynoms[2, 7] * (rho_i[2] + polynoms[2, 8]) ** 2 + polynoms[2, 9] * rho_i[0] * rho_i[2] + polynoms[2, 10] ) FAPAR = ((polynoms[0, 0] * g2) - polynoms[0, 1] * g1 - polynoms[0, 2]) / ( (polynoms[0, 3] - g1) ** 2 + (polynoms[0, 4] - g2) ** 2 + polynoms[0, 5] ) result.putpixel((x, y), get_color_fapar(FAPAR, rho_i)) result.save('fapar.png') root = etree.Element("kml") doc = etree.SubElement(root, "Document") start_x = 550 start_y = 900 width = 30 height = 30 result = result.crop((start_x, start_y, start_x+width, start_y+height)) for x in tqdm(range(result.size[0]), desc="KML"): for y in range(result.size[1]): color = result.getpixel((x, y)) if color == (0, 0, 0): continue coord = pixel_to_lat_lot(x+x0+start_x, y+y0+start_y, mtl_data) neighbour_r = pixel_to_lat_lot(x+1+x0+start_x, y+y0+start_y, mtl_data) neighbour_d = pixel_to_lat_lot(x+x0+start_x, y+1+y0+start_y, mtl_data) neighbour_rd = pixel_to_lat_lot(x+1+x0+start_x, y+1+y0+start_y, mtl_data) placemark = etree.SubElement(doc, "Placemark") etree.SubElement(placemark, "name").text = f"{x}_{y}" etree.SubElement(placemark, "styleUrl").text = f"#style_{x}_{y}" polygon = etree.SubElement(placemark, "Polygon") outer = etree.SubElement(polygon, "outerBoundaryIs") linearring = etree.SubElement(outer, "LinearRing") coordinates = etree.SubElement(linearring, "coordinates") coordinates.text = f"{coord[0]},{coord[1]},0 {neighbour_r[0]},{neighbour_r[1]},0 {neighbour_rd[0]},{neighbour_rd[1]},0 {neighbour_d[0]},{neighbour_d[1]},0 {coord[0]},{coord[1]},0" color = result.getpixel((x, y)) color = (color[2], color[1], color[0]) color = '%02x%02x%02x' % color color = "64" + color style = etree.SubElement(doc, "Style") style.set("id", f"style_{x}_{y}") polystyle = etree.SubElement(style, "PolyStyle") etree.SubElement(polystyle, "color").text = color with open("fapar.kml", "wb") as f: f.write(etree.tostring(root, pretty_print=True))