update
This commit is contained in:
@@ -0,0 +1,31 @@
|
||||
# Lab Assignment 2
|
||||
|
||||
Name: Joaquin Gottlebe
|
||||
Matrikelnummer: 829101
|
||||
|
||||
## Question 1
|
||||
|
||||

|
||||
|
||||
## Question 2
|
||||
|
||||

|
||||

|
||||
|
||||
The original Raster file included values from -125 C which are not realistic in germany. Also Maximum temperatures from 44 degrees are not common, but could be explainable through realy heat conductiv material on very small areas.
|
||||
|
||||
Between Water bodies and land bodies. and also the north and south of the area.
|
||||
|
||||
## Question 3
|
||||
|
||||

|
||||

|
||||
|
||||
The main differences of the map are the Bands and their display of it. For example the true color composite consists of band 2,3 and 4 in the order of RGB as 4,3,2. The false color composite consists of band 3,4 and 5 in the order ich which Vegetation is the most visible (5,4,3).
|
||||
|
||||
## Question 4
|
||||
|
||||

|
||||

|
||||
|
||||
I choose this study area because i was on this island Chiloe in Chile for a bit and noticed a lot of National parks and vegeation there and i wanted to know how this changed in these past years. These two images show difference in NDVI between 2013 and 2024 of the island Chiloe in Chile and surroundings. I chose NDVI as it shows the difference in vegetation very well.
|
||||
@@ -0,0 +1,23 @@
|
||||
.PHONY: all run_scripts
|
||||
|
||||
# Path to the Conda environment and scripts
|
||||
CONDA_ENV_NAME = gdal_env
|
||||
ACTIVATE_SCRIPT = ~/miniforge3/bin/activate
|
||||
SCRIPTS = top_of_atmosphere.py ndvi.py ndwi.py
|
||||
PANDOC=pandoc
|
||||
|
||||
all: run_scripts
|
||||
|
||||
run_scripts:
|
||||
@echo "Activating Conda environment and running scripts..."
|
||||
@source $(ACTIVATE_SCRIPT) && conda activate $(CONDA_ENV_NAME) && \
|
||||
python3 toa.py data/LC08_L1TP_193023_20170602_20170615_01_T1_MTL.txt toa data/LC08_L1TP_193023_20170602_20170615_01_T1_B2.TIF data/LC08_L1TP_193023_20170602_20170615_01_T1_B4.TIF data/LC08_L1TP_193023_20170602_20170615_01_T1_B3.TIF data/LC08_L1TP_193023_20170602_20170615_01_T1_B5.TIF && \
|
||||
python3 toa_radiance.py data/LC08_L1TP_193023_20170602_20170615_01_T1_MTL.txt toa_radiance data/LC08_L1TP_193023_20170602_20170615_01_T1_B10.TIF data/LC08_L1TP_193023_20170602_20170615_01_T1_B11.TIF && \
|
||||
python3 ndvi.py toa/LC08_L1TP_193023_20170602_20170615_01_T1_B4_toa.TIF toa/LC08_L1TP_193023_20170602_20170615_01_T1_B5_toa.TIF ndvi && \
|
||||
python3 ndwi.py toa/LC08_L1TP_193023_20170602_20170615_01_T1_B3_toa.TIF toa/LC08_L1TP_193023_20170602_20170615_01_T1_B5_toa.TIF ndwi && \
|
||||
python3 surface_temperature.py data/LC08_L1TP_193023_20170602_20170615_01_T1_MTL.txt toa_radiance/LC08_L1TP_193023_20170602_20170615_01_T1_B10_toa_radiance.TIF toa_radiance/LC08_L1TP_193023_20170602_20170615_01_T1_B11_toa_radiance.TIF surface_temperature && \
|
||||
python3 toa.py data2/LC08_L1TP_233089_20131224_20200912_02_T1_MTL.txt toa2 data2/LC08_L1TP_233089_20131224_20200912_02_T1_B2.TIF data2/LC08_L1TP_233089_20131224_20200912_02_T1_B4.TIF data2/LC08_L1TP_233089_20131224_20200912_02_T1_B3.TIF data2/LC08_L1TP_233089_20131224_20200912_02_T1_B5.TIF && \
|
||||
python3 toa.py data3/LC08_L1TP_233089_20240410_20240419_02_T1_MTL.txt toa3 data3/LC08_L1TP_233089_20240410_20240419_02_T1_B2.TIF data3/LC08_L1TP_233089_20240410_20240419_02_T1_B4.TIF data3/LC08_L1TP_233089_20240410_20240419_02_T1_B3.TIF data3/LC08_L1TP_233089_20240410_20240419_02_T1_B5.TIF && \
|
||||
python3 ndvi.py toa2/LC08_L1TP_233089_20131224_20200912_02_T1_B4_toa.TIF toa2/LC08_L1TP_233089_20131224_20200912_02_T1_B5_toa.TIF ndvi2 && \
|
||||
python3 ndvi.py toa3/LC08_L1TP_233089_20240410_20240419_02_T1_B4_toa.TIF toa3/LC08_L1TP_233089_20240410_20240419_02_T1_B5_toa.TIF ndvi3 && \
|
||||
$(PANDOC) Lab2.md -o rcm01_2425_lab2_gottlebe_829101.pdf
|
||||
@@ -0,0 +1,119 @@
|
||||
# Cleanup
|
||||
project = QgsProject.instance()
|
||||
layer_ids = list(project.mapLayers().keys())
|
||||
for layer_id in layer_ids:
|
||||
project.removeMapLayer(layer_id)
|
||||
|
||||
# Loading B10 Layer
|
||||
path_st_b10 = "/home/huaqo/dev/courses/2024_Remote_Sensing/lab2/false_color/false_color.tif"
|
||||
st_b10_layer = QgsRasterLayer(path_st_b10, "False Color Composite")
|
||||
if not st_b10_layer.isValid():
|
||||
print("st_b10 layer failed to load!")
|
||||
else:
|
||||
QgsProject.instance().addMapLayer(st_b10_layer)
|
||||
print("st_b10 Layer loaded!")
|
||||
|
||||
# Styling Layer
|
||||
st_b10_style_path = '/home/huaqo/dev/courses/2024_Remote_Sensing/styles/false_color.qml'
|
||||
if not st_b10_layer.loadNamedStyle(st_b10_style_path):
|
||||
print("Failed to load st_b10 style!")
|
||||
else:
|
||||
print("st_b10 style loaded!")
|
||||
|
||||
# Load layout
|
||||
layout = QgsPrintLayout(project)
|
||||
layout.initializeDefaults()
|
||||
|
||||
# Map
|
||||
map_item = QgsLayoutItemMap(layout)
|
||||
map_item.attemptMove(QgsLayoutPoint(20, 30, QgsUnitTypes.LayoutMillimeters)) # Added space between top and map
|
||||
map_item.attemptResize(QgsLayoutSize(200, 150, QgsUnitTypes.LayoutMillimeters)) # Reduced height for better spacing
|
||||
map_item.zoomToExtent(st_b10_layer.extent())
|
||||
layout.addLayoutItem(map_item)
|
||||
|
||||
# Add Coordinate Grid
|
||||
grid = map_item.grid()
|
||||
grid.setEnabled(True)
|
||||
grid.setIntervalX(100000) # Set interval for grid lines in map units
|
||||
grid.setIntervalY(100000)
|
||||
grid.setAnnotationEnabled(True)
|
||||
grid.setAnnotationPrecision(0) # Set annotation precision for grid labels
|
||||
grid.setFrameStyle(QgsLayoutItemMapGrid.Zebra) # Optional grid style
|
||||
|
||||
# Label
|
||||
title_label = QgsLayoutItemLabel(layout)
|
||||
title_label.setText("False Color Composite - Lab 2")
|
||||
title_label.setFont(QFont("Arial", 16))
|
||||
title_label.setHAlign(Qt.AlignCenter)
|
||||
title_label.attemptMove(QgsLayoutPoint(105, 10, QgsUnitTypes.LayoutMillimeters))
|
||||
title_label.adjustSizeToText()
|
||||
layout.addLayoutItem(title_label)
|
||||
|
||||
# Legend
|
||||
legend_item = QgsLayoutItemLegend(layout)
|
||||
legend_item.setLinkedMap(map_item)
|
||||
legend_item.setTitle("Legend")
|
||||
legend_item.setFrameEnabled(True)
|
||||
legend_item.attemptMove(QgsLayoutPoint(245, 30, QgsUnitTypes.LayoutMillimeters)) # Adjusted for better alignment and spacing
|
||||
layout.addLayoutItem(legend_item)
|
||||
|
||||
# Scalebar
|
||||
scalebar_item = QgsLayoutItemScaleBar(layout)
|
||||
scalebar_item.setStyle('Single Box') # Change to 'Single Box' or 'Double Box' for a proper scalebar
|
||||
scalebar_item.setLinkedMap(map_item)
|
||||
scalebar_item.setUnitLabel('m') # Add unit label to make it informative
|
||||
scalebar_item.setNumberOfSegments(4) # Specify the number of segments in the scalebar
|
||||
scalebar_item.setNumberOfSegmentsLeft(0) # Segments to the left of zero (if any)
|
||||
scalebar_item.setUnitsPerSegment(50000) # Set distance per segment in map units
|
||||
scalebar_item.setFont(QFont('Arial', 10)) # Set font size for better readability
|
||||
scalebar_item.setHeight(5) # Set the height of the scalebar
|
||||
scalebar_item.applyDefaultSize()
|
||||
scalebar_item.attemptMove(QgsLayoutPoint(20, 190, QgsUnitTypes.LayoutMillimeters)) # Moved below the map for better organization
|
||||
layout.addLayoutItem(scalebar_item)
|
||||
|
||||
# North Arrow
|
||||
north_arrow_item = QgsLayoutItemPicture(layout)
|
||||
north_arrow_item.setPicturePath('/home/huaqo/dev/courses/2024_Remote_Sensing/styles/north_arrow.svg')
|
||||
if not north_arrow_item.picturePath():
|
||||
print("North arrow image failed to load!")
|
||||
north_arrow_item.setReferencePoint(QgsLayoutItemPicture.UpperLeft)
|
||||
north_arrow_item.attemptMove(QgsLayoutPoint(30, 40, QgsUnitTypes.LayoutMillimeters)) # Adjusted placement for better visibility
|
||||
north_arrow_item.attemptResize(QgsLayoutSize(15, 15, QgsUnitTypes.LayoutMillimeters)) # Increased size for better visibility
|
||||
layout.addLayoutItem(north_arrow_item)
|
||||
|
||||
# Author
|
||||
info_label = QgsLayoutItemLabel(layout)
|
||||
info_label.setText("Author: Joaquin Gottlebe")
|
||||
info_label.setFont(QFont("Arial", 12))
|
||||
info_label.setHAlign(Qt.AlignLeft)
|
||||
info_label.attemptMove(QgsLayoutPoint(245, 100, QgsUnitTypes.LayoutMillimeters))
|
||||
info_label.adjustSizeToText()
|
||||
layout.addLayoutItem(info_label)
|
||||
|
||||
# Author
|
||||
info_label = QgsLayoutItemLabel(layout)
|
||||
info_label.setText("Date: 16.11.2025")
|
||||
info_label.setFont(QFont("Arial", 12))
|
||||
info_label.setHAlign(Qt.AlignLeft)
|
||||
info_label.attemptMove(QgsLayoutPoint(245, 110, QgsUnitTypes.LayoutMillimeters))
|
||||
info_label.adjustSizeToText()
|
||||
layout.addLayoutItem(info_label)
|
||||
|
||||
# Coordinate System
|
||||
info_label = QgsLayoutItemLabel(layout)
|
||||
info_label.setText("CRS: EPSG 32633")
|
||||
info_label.setFont(QFont("Arial", 12))
|
||||
info_label.setHAlign(Qt.AlignLeft)
|
||||
info_label.attemptMove(QgsLayoutPoint(245, 120, QgsUnitTypes.LayoutMillimeters))
|
||||
info_label.adjustSizeToText()
|
||||
layout.addLayoutItem(info_label)
|
||||
|
||||
|
||||
# Export
|
||||
export_path = "/home/huaqo/dev/courses/2024_Remote_Sensing/lab2/maps/false_color.png"
|
||||
exporter = QgsLayoutExporter(layout)
|
||||
export_result = exporter.exportToImage(export_path, QgsLayoutExporter.ImageExportSettings())
|
||||
if export_result != QgsLayoutExporter.Success:
|
||||
print("Failed to export map!")
|
||||
else:
|
||||
print("Map exported to:", export_path)
|
||||
@@ -0,0 +1,2 @@
|
||||
mktextfm ecrm1000
|
||||
mktextfm ecrm1000
|
||||
@@ -0,0 +1,73 @@
|
||||
from osgeo import gdal
|
||||
import numpy as np
|
||||
import sys
|
||||
import os
|
||||
import re
|
||||
|
||||
def tif_to_array(path):
|
||||
data = gdal.Open(path)
|
||||
if data is None:
|
||||
raise FileNotFoundError(f"Cannot open file: {path}")
|
||||
band = data.GetRasterBand(1)
|
||||
array = band.ReadAsArray().astype(np.float32)
|
||||
return data, array
|
||||
|
||||
def handle_nodata(array):
|
||||
clean_array = np.where((array < 0) | (array > 1), np.nan, array)
|
||||
return clean_array
|
||||
|
||||
def ndvi_calc(NIR,RED):
|
||||
ndvi_array = (NIR - RED) / (NIR + RED + 1e-10)
|
||||
return ndvi_array
|
||||
|
||||
def save_tif(array,data,output_path):
|
||||
driver = gdal.GetDriverByName('GTiff')
|
||||
rows, cols = array.shape
|
||||
out_data = driver.Create(output_path, cols, rows, 1, gdal.GDT_Float32)
|
||||
out_data.SetGeoTransform(data.GetGeoTransform())
|
||||
out_data.SetProjection(data.GetProjection())
|
||||
out_band = out_data.GetRasterBand(1)
|
||||
out_band.WriteArray(array)
|
||||
out_band.SetNoDataValue(-9999)
|
||||
out_band.FlushCache()
|
||||
out_data = None
|
||||
|
||||
def get_output_path(output_dir,input_path):
|
||||
basename = os.path.basename(input_path)
|
||||
basename_no_ext = os.path.splitext(basename)[0]
|
||||
basename_no_ext = re.sub(r'_B[1-9]|_B10|_B11', '', basename_no_ext)
|
||||
path = os.path.join(output_dir, f"{basename_no_ext}_ndvi.TIF")
|
||||
return path
|
||||
|
||||
def print_min_max(array, label):
|
||||
print(label," Min:", np.nanmin(array), " Max:",np.nanmax(array))
|
||||
|
||||
def main():
|
||||
if len(sys.argv) != 4:
|
||||
print("Usage: python3 script.py <red_band_path> <nir_band_path> <output_directory>")
|
||||
sys.exit(1)
|
||||
|
||||
red_path = sys.argv[1]
|
||||
nir_path = sys.argv[2]
|
||||
output_dir = sys.argv[3]
|
||||
|
||||
red_data, red_array = tif_to_array(red_path)
|
||||
nir_data, nir_array = tif_to_array(nir_path)
|
||||
red_array = handle_nodata(red_array)
|
||||
nir_array = handle_nodata(nir_array)
|
||||
|
||||
ndvi_array = ndvi_calc(nir_array, red_array)
|
||||
|
||||
if not os.path.exists(output_dir):
|
||||
os.makedirs(output_dir)
|
||||
|
||||
output_path = get_output_path(output_dir,red_path)
|
||||
|
||||
save_tif(ndvi_array, red_data, output_path)
|
||||
|
||||
print_min_max(red_array, "RED")
|
||||
print_min_max(nir_array, "NIR")
|
||||
print_min_max(ndvi_array, "NDVI")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,119 @@
|
||||
# Cleanup
|
||||
project = QgsProject.instance()
|
||||
layer_ids = list(project.mapLayers().keys())
|
||||
for layer_id in layer_ids:
|
||||
project.removeMapLayer(layer_id)
|
||||
|
||||
# Loading NDVI Layer
|
||||
path_ndvi = "/home/huaqo/dev/courses/2024_Remote_Sensing/lab2/ndvi3/LC08_L1TP_233089_20240410_20240419_02_T1_toa_ndvi.TIF"
|
||||
ndvi_layer = QgsRasterLayer(path_ndvi, "NDVI")
|
||||
if not ndvi_layer.isValid():
|
||||
print("NDVI layer failed to load!")
|
||||
else:
|
||||
QgsProject.instance().addMapLayer(ndvi_layer)
|
||||
print("NDVI Layer loaded!")
|
||||
|
||||
# Styling NDVI Layer
|
||||
ndvi_style_path = '/home/huaqo/dev/courses/2024_Remote_Sensing/styles/ndvi.qml'
|
||||
if not ndvi_layer.loadNamedStyle(ndvi_style_path):
|
||||
print("Failed to load NDVI style!")
|
||||
else:
|
||||
print("NDVI style loaded!")
|
||||
|
||||
# Load layout
|
||||
layout = QgsPrintLayout(project)
|
||||
layout.initializeDefaults()
|
||||
|
||||
# Map
|
||||
map_item = QgsLayoutItemMap(layout)
|
||||
map_item.attemptMove(QgsLayoutPoint(20, 30, QgsUnitTypes.LayoutMillimeters)) # Added space between top and map
|
||||
map_item.attemptResize(QgsLayoutSize(200, 150, QgsUnitTypes.LayoutMillimeters)) # Reduced height for better spacing
|
||||
map_item.zoomToExtent(ndvi_layer.extent())
|
||||
layout.addLayoutItem(map_item)
|
||||
|
||||
# Add Coordinate Grid
|
||||
grid = map_item.grid()
|
||||
grid.setEnabled(True)
|
||||
grid.setIntervalX(100000) # Set interval for grid lines in map units
|
||||
grid.setIntervalY(100000)
|
||||
grid.setAnnotationEnabled(True)
|
||||
grid.setAnnotationPrecision(0) # Set annotation precision for grid labels
|
||||
grid.setFrameStyle(QgsLayoutItemMapGrid.Zebra) # Optional grid style
|
||||
|
||||
# Label
|
||||
title_label = QgsLayoutItemLabel(layout)
|
||||
title_label.setText("NDVI Chiloe 2024 - Lab 2")
|
||||
title_label.setFont(QFont("Arial", 16))
|
||||
title_label.setHAlign(Qt.AlignCenter)
|
||||
title_label.attemptMove(QgsLayoutPoint(105, 10, QgsUnitTypes.LayoutMillimeters))
|
||||
title_label.adjustSizeToText()
|
||||
layout.addLayoutItem(title_label)
|
||||
|
||||
# Legend
|
||||
legend_item = QgsLayoutItemLegend(layout)
|
||||
legend_item.setLinkedMap(map_item)
|
||||
legend_item.setTitle("Legend")
|
||||
legend_item.setFrameEnabled(True)
|
||||
legend_item.attemptMove(QgsLayoutPoint(245, 30, QgsUnitTypes.LayoutMillimeters)) # Adjusted for better alignment and spacing
|
||||
layout.addLayoutItem(legend_item)
|
||||
|
||||
# Scalebar
|
||||
scalebar_item = QgsLayoutItemScaleBar(layout)
|
||||
scalebar_item.setStyle('Single Box') # Change to 'Single Box' or 'Double Box' for a proper scalebar
|
||||
scalebar_item.setLinkedMap(map_item)
|
||||
scalebar_item.setUnitLabel('m') # Add unit label to make it informative
|
||||
scalebar_item.setNumberOfSegments(4) # Specify the number of segments in the scalebar
|
||||
scalebar_item.setNumberOfSegmentsLeft(0) # Segments to the left of zero (if any)
|
||||
scalebar_item.setUnitsPerSegment(50000) # Set distance per segment in map units
|
||||
scalebar_item.setFont(QFont('Arial', 10)) # Set font size for better readability
|
||||
scalebar_item.setHeight(5) # Set the height of the scalebar
|
||||
scalebar_item.applyDefaultSize()
|
||||
scalebar_item.attemptMove(QgsLayoutPoint(20, 190, QgsUnitTypes.LayoutMillimeters)) # Moved below the map for better organization
|
||||
layout.addLayoutItem(scalebar_item)
|
||||
|
||||
# North Arrow
|
||||
north_arrow_item = QgsLayoutItemPicture(layout)
|
||||
north_arrow_item.setPicturePath('/home/huaqo/dev/courses/2024_Remote_Sensing/styles/north_arrow.svg')
|
||||
if not north_arrow_item.picturePath():
|
||||
print("North arrow image failed to load!")
|
||||
north_arrow_item.setReferencePoint(QgsLayoutItemPicture.UpperLeft)
|
||||
north_arrow_item.attemptMove(QgsLayoutPoint(30, 40, QgsUnitTypes.LayoutMillimeters)) # Adjusted placement for better visibility
|
||||
north_arrow_item.attemptResize(QgsLayoutSize(15, 15, QgsUnitTypes.LayoutMillimeters)) # Increased size for better visibility
|
||||
layout.addLayoutItem(north_arrow_item)
|
||||
|
||||
# Author
|
||||
info_label = QgsLayoutItemLabel(layout)
|
||||
info_label.setText("Author: Joaquin Gottlebe")
|
||||
info_label.setFont(QFont("Arial", 12))
|
||||
info_label.setHAlign(Qt.AlignLeft)
|
||||
info_label.attemptMove(QgsLayoutPoint(245, 100, QgsUnitTypes.LayoutMillimeters))
|
||||
info_label.adjustSizeToText()
|
||||
layout.addLayoutItem(info_label)
|
||||
|
||||
# Author
|
||||
info_label = QgsLayoutItemLabel(layout)
|
||||
info_label.setText("Date: 16.11.2025")
|
||||
info_label.setFont(QFont("Arial", 12))
|
||||
info_label.setHAlign(Qt.AlignLeft)
|
||||
info_label.attemptMove(QgsLayoutPoint(245, 110, QgsUnitTypes.LayoutMillimeters))
|
||||
info_label.adjustSizeToText()
|
||||
layout.addLayoutItem(info_label)
|
||||
|
||||
# Coordinate System
|
||||
info_label = QgsLayoutItemLabel(layout)
|
||||
info_label.setText("CRS: EPSG 32633")
|
||||
info_label.setFont(QFont("Arial", 12))
|
||||
info_label.setHAlign(Qt.AlignLeft)
|
||||
info_label.attemptMove(QgsLayoutPoint(245, 120, QgsUnitTypes.LayoutMillimeters))
|
||||
info_label.adjustSizeToText()
|
||||
layout.addLayoutItem(info_label)
|
||||
|
||||
|
||||
# Export
|
||||
export_path = "/home/huaqo/dev/courses/2024_Remote_Sensing/lab2/maps/ndvi_chiloe_2024.png"
|
||||
exporter = QgsLayoutExporter(layout)
|
||||
export_result = exporter.exportToImage(export_path, QgsLayoutExporter.ImageExportSettings())
|
||||
if export_result != QgsLayoutExporter.Success:
|
||||
print("Failed to export map!")
|
||||
else:
|
||||
print("Map exported to:", export_path)
|
||||
@@ -0,0 +1,139 @@
|
||||
# Cleanup
|
||||
project = QgsProject.instance()
|
||||
layer_ids = list(project.mapLayers().keys())
|
||||
for layer_id in layer_ids:
|
||||
project.removeMapLayer(layer_id)
|
||||
|
||||
# Loading NDVI Layer
|
||||
path_ndvi = "/home/huaqo/dev/courses/2024_Remote_Sensing/lab2/ndvi/LC08_L1TP_193023_20170602_20170615_01_T1_toa_ndvi.TIF"
|
||||
ndvi_layer = QgsRasterLayer(path_ndvi, "NDVI")
|
||||
if not ndvi_layer.isValid():
|
||||
print("NDVI layer failed to load!")
|
||||
else:
|
||||
QgsProject.instance().addMapLayer(ndvi_layer)
|
||||
print("NDVI Layer loaded!")
|
||||
|
||||
# Loading Additional Layer
|
||||
path_overlay = "/home/huaqo/dev/courses/2024_Remote_Sensing/lab2/ndwi/LC08_L1TP_193023_20170602_20170615_01_T1_toa_ndwi.TIF" # Replace with your actual layer path
|
||||
ndwi_layer = QgsRasterLayer(path_overlay, "NDWI")
|
||||
if not ndwi_layer.isValid():
|
||||
print("NDWI layer failed to load!")
|
||||
else:
|
||||
QgsProject.instance().addMapLayer(ndwi_layer)
|
||||
print("NDWI Layer loaded!")
|
||||
|
||||
# Styling NDVI Layer
|
||||
ndvi_style_path = '/home/huaqo/dev/courses/2024_Remote_Sensing/styles/ndvi.qml'
|
||||
if not ndvi_layer.loadNamedStyle(ndvi_style_path):
|
||||
print("Failed to load NDVI style!")
|
||||
else:
|
||||
print("NDVI style loaded!")
|
||||
|
||||
ndwi_style_path = '/home/huaqo/dev/courses/2024_Remote_Sensing/styles/ndwi.qml'
|
||||
if not ndwi_layer.loadNamedStyle(ndwi_style_path):
|
||||
print("Failed to load NDWI style!")
|
||||
else:
|
||||
print("NDWI style loaded!")
|
||||
|
||||
# Reference system (optional, uncomment if needed)
|
||||
# crs = QgsCoordinateReferenceSystem("EPSG:4326")
|
||||
# crsSrc = QgsCoordinateReferenceSystem("EPSG:32633")
|
||||
# crsDest = QgsCoordinateReferenceSystem("EPSG:4326")
|
||||
|
||||
# Load layout
|
||||
layout = QgsPrintLayout(project)
|
||||
layout.initializeDefaults()
|
||||
|
||||
# Map
|
||||
map_item = QgsLayoutItemMap(layout)
|
||||
map_item.attemptMove(QgsLayoutPoint(20, 30, QgsUnitTypes.LayoutMillimeters)) # Added space between top and map
|
||||
map_item.attemptResize(QgsLayoutSize(200, 150, QgsUnitTypes.LayoutMillimeters)) # Reduced height for better spacing
|
||||
map_item.zoomToExtent(ndvi_layer.extent())
|
||||
layout.addLayoutItem(map_item)
|
||||
|
||||
# Add Coordinate Grid
|
||||
grid = map_item.grid()
|
||||
grid.setEnabled(True)
|
||||
grid.setIntervalX(100000) # Set interval for grid lines in map units
|
||||
grid.setIntervalY(100000)
|
||||
grid.setAnnotationEnabled(True)
|
||||
grid.setAnnotationPrecision(0) # Set annotation precision for grid labels
|
||||
grid.setFrameStyle(QgsLayoutItemMapGrid.Zebra) # Optional grid style
|
||||
|
||||
# Label
|
||||
title_label = QgsLayoutItemLabel(layout)
|
||||
title_label.setText("NDVI & NDWI - Lab 2")
|
||||
title_label.setFont(QFont("Arial", 16))
|
||||
title_label.setHAlign(Qt.AlignCenter)
|
||||
title_label.attemptMove(QgsLayoutPoint(105, 10, QgsUnitTypes.LayoutMillimeters))
|
||||
title_label.adjustSizeToText()
|
||||
layout.addLayoutItem(title_label)
|
||||
|
||||
# Legend
|
||||
legend_item = QgsLayoutItemLegend(layout)
|
||||
legend_item.setLinkedMap(map_item)
|
||||
legend_item.setTitle("Legend")
|
||||
legend_item.setFrameEnabled(True)
|
||||
legend_item.attemptMove(QgsLayoutPoint(245, 30, QgsUnitTypes.LayoutMillimeters)) # Adjusted for better alignment and spacing
|
||||
layout.addLayoutItem(legend_item)
|
||||
|
||||
# Scalebar
|
||||
scalebar_item = QgsLayoutItemScaleBar(layout)
|
||||
scalebar_item.setStyle('Single Box') # Change to 'Single Box' or 'Double Box' for a proper scalebar
|
||||
scalebar_item.setLinkedMap(map_item)
|
||||
scalebar_item.setUnitLabel('m') # Add unit label to make it informative
|
||||
scalebar_item.setNumberOfSegments(4) # Specify the number of segments in the scalebar
|
||||
scalebar_item.setNumberOfSegmentsLeft(0) # Segments to the left of zero (if any)
|
||||
scalebar_item.setUnitsPerSegment(50000) # Set distance per segment in map units
|
||||
scalebar_item.setFont(QFont('Arial', 10)) # Set font size for better readability
|
||||
scalebar_item.setHeight(5) # Set the height of the scalebar
|
||||
scalebar_item.applyDefaultSize()
|
||||
scalebar_item.attemptMove(QgsLayoutPoint(20, 190, QgsUnitTypes.LayoutMillimeters)) # Moved below the map for better organization
|
||||
layout.addLayoutItem(scalebar_item)
|
||||
|
||||
# North Arrow
|
||||
north_arrow_item = QgsLayoutItemPicture(layout)
|
||||
north_arrow_item.setPicturePath('/home/huaqo/dev/courses/2024_Remote_Sensing/styles/north_arrow.svg')
|
||||
if not north_arrow_item.picturePath():
|
||||
print("North arrow image failed to load!")
|
||||
north_arrow_item.setReferencePoint(QgsLayoutItemPicture.UpperLeft)
|
||||
north_arrow_item.attemptMove(QgsLayoutPoint(30, 40, QgsUnitTypes.LayoutMillimeters)) # Adjusted placement for better visibility
|
||||
north_arrow_item.attemptResize(QgsLayoutSize(15, 15, QgsUnitTypes.LayoutMillimeters)) # Increased size for better visibility
|
||||
layout.addLayoutItem(north_arrow_item)
|
||||
|
||||
# Author
|
||||
info_label = QgsLayoutItemLabel(layout)
|
||||
info_label.setText("Author: Joaquin Gottlebe")
|
||||
info_label.setFont(QFont("Arial", 12))
|
||||
info_label.setHAlign(Qt.AlignLeft)
|
||||
info_label.attemptMove(QgsLayoutPoint(245, 100, QgsUnitTypes.LayoutMillimeters))
|
||||
info_label.adjustSizeToText()
|
||||
layout.addLayoutItem(info_label)
|
||||
|
||||
# Author
|
||||
info_label = QgsLayoutItemLabel(layout)
|
||||
info_label.setText("Date: 16.11.2025")
|
||||
info_label.setFont(QFont("Arial", 12))
|
||||
info_label.setHAlign(Qt.AlignLeft)
|
||||
info_label.attemptMove(QgsLayoutPoint(245, 110, QgsUnitTypes.LayoutMillimeters))
|
||||
info_label.adjustSizeToText()
|
||||
layout.addLayoutItem(info_label)
|
||||
|
||||
# Coordinate System
|
||||
info_label = QgsLayoutItemLabel(layout)
|
||||
info_label.setText("CRS: EPSG 32633")
|
||||
info_label.setFont(QFont("Arial", 12))
|
||||
info_label.setHAlign(Qt.AlignLeft)
|
||||
info_label.attemptMove(QgsLayoutPoint(245, 120, QgsUnitTypes.LayoutMillimeters))
|
||||
info_label.adjustSizeToText()
|
||||
layout.addLayoutItem(info_label)
|
||||
|
||||
|
||||
# Export
|
||||
export_path = "/home/huaqo/dev/courses/2024_Remote_Sensing/lab2/maps/ndvi_ndwi.png"
|
||||
exporter = QgsLayoutExporter(layout)
|
||||
export_result = exporter.exportToImage(export_path, QgsLayoutExporter.ImageExportSettings())
|
||||
if export_result != QgsLayoutExporter.Success:
|
||||
print("Failed to export map!")
|
||||
else:
|
||||
print("Map exported to:", export_path)
|
||||
@@ -0,0 +1,81 @@
|
||||
from osgeo import gdal
|
||||
import numpy as np
|
||||
import sys
|
||||
import os
|
||||
import re
|
||||
|
||||
|
||||
def tif_to_array(path):
|
||||
data = gdal.Open(path)
|
||||
if data is None:
|
||||
raise FileNotFoundError(f"Cannot open file: {path}")
|
||||
band = data.GetRasterBand(1)
|
||||
array = band.ReadAsArray().astype(np.float32)
|
||||
return data, array
|
||||
|
||||
|
||||
def handle_nodata(array):
|
||||
clean_array = np.where((array < 0) | (array > 1), np.nan, array)
|
||||
return clean_array
|
||||
|
||||
|
||||
def ndwi_calc(GREEN, NIR):
|
||||
ndwi_array = (GREEN - NIR) / (GREEN + NIR + 1e-10)
|
||||
return ndwi_array
|
||||
|
||||
|
||||
def save_tif(array, data, output_path):
|
||||
driver = gdal.GetDriverByName('GTiff')
|
||||
rows, cols = array.shape
|
||||
out_data = driver.Create(output_path, cols, rows, 1, gdal.GDT_Float32)
|
||||
out_data.SetGeoTransform(data.GetGeoTransform())
|
||||
out_data.SetProjection(data.GetProjection())
|
||||
out_band = out_data.GetRasterBand(1)
|
||||
out_band.WriteArray(array)
|
||||
out_band.SetNoDataValue(-9999)
|
||||
out_band.FlushCache()
|
||||
out_data = None
|
||||
|
||||
|
||||
def get_output_path(output_dir, input_path):
|
||||
basename = os.path.basename(input_path)
|
||||
basename_no_ext = os.path.splitext(basename)[0]
|
||||
basename_no_ext = re.sub(r'_B[1-9]|_B10|_B11', '', basename_no_ext)
|
||||
path = os.path.join(output_dir, f"{basename_no_ext}_ndwi.TIF")
|
||||
return path
|
||||
|
||||
|
||||
def print_min_max(array, label):
|
||||
print(label, " Min:", np.nanmin(array), " Max:", np.nanmax(array))
|
||||
|
||||
|
||||
def main():
|
||||
if len(sys.argv) != 4:
|
||||
print("Usage: python3 script.py <green_band_path> <nir_band_path> <output_directory>")
|
||||
sys.exit(1)
|
||||
|
||||
green_path = sys.argv[1]
|
||||
nir_path = sys.argv[2]
|
||||
output_dir = sys.argv[3]
|
||||
|
||||
green_data, green_array = tif_to_array(green_path)
|
||||
nir_data, nir_array = tif_to_array(nir_path)
|
||||
green_array = handle_nodata(green_array)
|
||||
nir_array = handle_nodata(nir_array)
|
||||
|
||||
ndwi_array = ndwi_calc(green_array, nir_array)
|
||||
|
||||
if not os.path.exists(output_dir):
|
||||
os.makedirs(output_dir)
|
||||
|
||||
output_path = get_output_path(output_dir, green_path)
|
||||
|
||||
save_tif(ndwi_array, green_data, output_path)
|
||||
|
||||
print_min_max(nir_array, "NIR")
|
||||
print_min_max(green_array, "GREEN")
|
||||
print_min_max(ndwi_array, "NDWI")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,81 @@
|
||||
import sys
|
||||
import os
|
||||
from osgeo import gdal
|
||||
import numpy as np
|
||||
import math
|
||||
|
||||
def load_metadata(metadata_path):
|
||||
K_CONSTANTS = {}
|
||||
with open(metadata_path, "r") as file:
|
||||
metadata_lines = file.readlines()
|
||||
for line in metadata_lines:
|
||||
if "K1_CONSTANT_BAND_10" in line or "K2_CONSTANT_BAND_10" in line or \
|
||||
"K1_CONSTANT_BAND_11" in line or "K2_CONSTANT_BAND_11" in line:
|
||||
variable, value = line.split(" = ")
|
||||
K_CONSTANTS[variable.strip()] = float(value.strip())
|
||||
return K_CONSTANTS
|
||||
|
||||
def load_band(band_path):
|
||||
band_data = gdal.Open(band_path)
|
||||
band = band_data.GetRasterBand(1)
|
||||
band_array = band.ReadAsArray().astype(np.float32)
|
||||
no_data_value = band.GetNoDataValue()
|
||||
band_array = np.where(band_array == no_data_value, np.nan, band_array)
|
||||
return band_data, band_array
|
||||
|
||||
def calculate_surface_temperature(band_array, K1_CONSTANT, K2_CONSTANT):
|
||||
epsilon = 1e-10
|
||||
result_array = (K2_CONSTANT / np.log((K1_CONSTANT / (band_array + epsilon)) + 1)) - 273.15
|
||||
return result_array
|
||||
|
||||
def save_result(output_directory, band_path, result_array, band_data):
|
||||
if not os.path.exists(output_directory):
|
||||
os.makedirs(output_directory)
|
||||
driver = gdal.GetDriverByName('GTiff')
|
||||
rows, cols = result_array.shape
|
||||
band_filename = os.path.basename(band_path)
|
||||
output_filename = os.path.splitext(band_filename)[0] + '_surfacetemp.TIF'
|
||||
output_path = os.path.join(output_directory, output_filename)
|
||||
out_data = driver.Create(output_path, cols, rows, 1, gdal.GDT_Float32)
|
||||
out_data.SetGeoTransform(band_data.GetGeoTransform())
|
||||
out_data.SetProjection(band_data.GetProjection())
|
||||
out_band = out_data.GetRasterBand(1)
|
||||
out_band.WriteArray(result_array)
|
||||
out_band.SetNoDataValue(0)
|
||||
out_band.FlushCache()
|
||||
out_data = None
|
||||
|
||||
def main():
|
||||
metadata_path = sys.argv[1]
|
||||
band_paths = {
|
||||
'BAND_10': sys.argv[2],
|
||||
'BAND_11': sys.argv[3]
|
||||
}
|
||||
output_directory = sys.argv[4]
|
||||
|
||||
# Load metadata
|
||||
K_CONSTANTS = load_metadata(metadata_path)
|
||||
print(K_CONSTANTS)
|
||||
|
||||
# Process each band
|
||||
for band_key, band_path in band_paths.items():
|
||||
# Load band
|
||||
band_data, band_array = load_band(band_path)
|
||||
|
||||
# Get K1 and K2 constants
|
||||
K1_CONSTANT = K_CONSTANTS[f'K1_CONSTANT_{band_key}']
|
||||
K2_CONSTANT = K_CONSTANTS[f'K2_CONSTANT_{band_key}']
|
||||
|
||||
# Calculate surface temperature
|
||||
result_array = calculate_surface_temperature(band_array, K1_CONSTANT, K2_CONSTANT)
|
||||
|
||||
# Save the result
|
||||
save_result(output_directory, band_path, result_array, band_data)
|
||||
|
||||
# Print min and max of the arrays
|
||||
print(f"{band_key} Min, Max:", np.nanmin(band_array), np.nanmax(band_array))
|
||||
print(f"{band_key} Surface Temp Min, Max:", np.nanmin(result_array), np.nanmax(result_array))
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
@@ -0,0 +1,119 @@
|
||||
# Cleanup
|
||||
project = QgsProject.instance()
|
||||
layer_ids = list(project.mapLayers().keys())
|
||||
for layer_id in layer_ids:
|
||||
project.removeMapLayer(layer_id)
|
||||
|
||||
# Loading B10 Layer
|
||||
path_st_b10 = "/home/huaqo/dev/courses/2024_Remote_Sensing/lab2/surface_temperature/LC08_L1TP_193023_20170602_20170615_01_T1_B11_toa_radiance_surfacetemp.TIF"
|
||||
st_b10_layer = QgsRasterLayer(path_st_b10, "Surf. Temp. B11")
|
||||
if not st_b10_layer.isValid():
|
||||
print("st_b10 layer failed to load!")
|
||||
else:
|
||||
QgsProject.instance().addMapLayer(st_b10_layer)
|
||||
print("st_b10 Layer loaded!")
|
||||
|
||||
# Styling Layer
|
||||
st_b10_style_path = '/home/huaqo/dev/courses/2024_Remote_Sensing/styles/surface_temperature.qml'
|
||||
if not st_b10_layer.loadNamedStyle(st_b10_style_path):
|
||||
print("Failed to load st_b10 style!")
|
||||
else:
|
||||
print("st_b10 style loaded!")
|
||||
|
||||
# Load layout
|
||||
layout = QgsPrintLayout(project)
|
||||
layout.initializeDefaults()
|
||||
|
||||
# Map
|
||||
map_item = QgsLayoutItemMap(layout)
|
||||
map_item.attemptMove(QgsLayoutPoint(20, 30, QgsUnitTypes.LayoutMillimeters)) # Added space between top and map
|
||||
map_item.attemptResize(QgsLayoutSize(200, 150, QgsUnitTypes.LayoutMillimeters)) # Reduced height for better spacing
|
||||
map_item.zoomToExtent(st_b10_layer.extent())
|
||||
layout.addLayoutItem(map_item)
|
||||
|
||||
# Add Coordinate Grid
|
||||
grid = map_item.grid()
|
||||
grid.setEnabled(True)
|
||||
grid.setIntervalX(100000) # Set interval for grid lines in map units
|
||||
grid.setIntervalY(100000)
|
||||
grid.setAnnotationEnabled(True)
|
||||
grid.setAnnotationPrecision(0) # Set annotation precision for grid labels
|
||||
grid.setFrameStyle(QgsLayoutItemMapGrid.Zebra) # Optional grid style
|
||||
|
||||
# Label
|
||||
title_label = QgsLayoutItemLabel(layout)
|
||||
title_label.setText("Surace Temperautre B11 - Lab 2")
|
||||
title_label.setFont(QFont("Arial", 16))
|
||||
title_label.setHAlign(Qt.AlignCenter)
|
||||
title_label.attemptMove(QgsLayoutPoint(105, 10, QgsUnitTypes.LayoutMillimeters))
|
||||
title_label.adjustSizeToText()
|
||||
layout.addLayoutItem(title_label)
|
||||
|
||||
# Legend
|
||||
legend_item = QgsLayoutItemLegend(layout)
|
||||
legend_item.setLinkedMap(map_item)
|
||||
legend_item.setTitle("Legend")
|
||||
legend_item.setFrameEnabled(True)
|
||||
legend_item.attemptMove(QgsLayoutPoint(245, 30, QgsUnitTypes.LayoutMillimeters)) # Adjusted for better alignment and spacing
|
||||
layout.addLayoutItem(legend_item)
|
||||
|
||||
# Scalebar
|
||||
scalebar_item = QgsLayoutItemScaleBar(layout)
|
||||
scalebar_item.setStyle('Single Box') # Change to 'Single Box' or 'Double Box' for a proper scalebar
|
||||
scalebar_item.setLinkedMap(map_item)
|
||||
scalebar_item.setUnitLabel('m') # Add unit label to make it informative
|
||||
scalebar_item.setNumberOfSegments(4) # Specify the number of segments in the scalebar
|
||||
scalebar_item.setNumberOfSegmentsLeft(0) # Segments to the left of zero (if any)
|
||||
scalebar_item.setUnitsPerSegment(50000) # Set distance per segment in map units
|
||||
scalebar_item.setFont(QFont('Arial', 10)) # Set font size for better readability
|
||||
scalebar_item.setHeight(5) # Set the height of the scalebar
|
||||
scalebar_item.applyDefaultSize()
|
||||
scalebar_item.attemptMove(QgsLayoutPoint(20, 190, QgsUnitTypes.LayoutMillimeters)) # Moved below the map for better organization
|
||||
layout.addLayoutItem(scalebar_item)
|
||||
|
||||
# North Arrow
|
||||
north_arrow_item = QgsLayoutItemPicture(layout)
|
||||
north_arrow_item.setPicturePath('/home/huaqo/dev/courses/2024_Remote_Sensing/styles/north_arrow.svg')
|
||||
if not north_arrow_item.picturePath():
|
||||
print("North arrow image failed to load!")
|
||||
north_arrow_item.setReferencePoint(QgsLayoutItemPicture.UpperLeft)
|
||||
north_arrow_item.attemptMove(QgsLayoutPoint(30, 40, QgsUnitTypes.LayoutMillimeters)) # Adjusted placement for better visibility
|
||||
north_arrow_item.attemptResize(QgsLayoutSize(15, 15, QgsUnitTypes.LayoutMillimeters)) # Increased size for better visibility
|
||||
layout.addLayoutItem(north_arrow_item)
|
||||
|
||||
# Author
|
||||
info_label = QgsLayoutItemLabel(layout)
|
||||
info_label.setText("Author: Joaquin Gottlebe")
|
||||
info_label.setFont(QFont("Arial", 12))
|
||||
info_label.setHAlign(Qt.AlignLeft)
|
||||
info_label.attemptMove(QgsLayoutPoint(245, 100, QgsUnitTypes.LayoutMillimeters))
|
||||
info_label.adjustSizeToText()
|
||||
layout.addLayoutItem(info_label)
|
||||
|
||||
# Author
|
||||
info_label = QgsLayoutItemLabel(layout)
|
||||
info_label.setText("Date: 16.11.2025")
|
||||
info_label.setFont(QFont("Arial", 12))
|
||||
info_label.setHAlign(Qt.AlignLeft)
|
||||
info_label.attemptMove(QgsLayoutPoint(245, 110, QgsUnitTypes.LayoutMillimeters))
|
||||
info_label.adjustSizeToText()
|
||||
layout.addLayoutItem(info_label)
|
||||
|
||||
# Coordinate System
|
||||
info_label = QgsLayoutItemLabel(layout)
|
||||
info_label.setText("CRS: EPSG 32633")
|
||||
info_label.setFont(QFont("Arial", 12))
|
||||
info_label.setHAlign(Qt.AlignLeft)
|
||||
info_label.attemptMove(QgsLayoutPoint(245, 120, QgsUnitTypes.LayoutMillimeters))
|
||||
info_label.adjustSizeToText()
|
||||
layout.addLayoutItem(info_label)
|
||||
|
||||
|
||||
# Export
|
||||
export_path = "/home/huaqo/dev/courses/2024_Remote_Sensing/lab2/maps/surface_temp_b11.png"
|
||||
exporter = QgsLayoutExporter(layout)
|
||||
export_result = exporter.exportToImage(export_path, QgsLayoutExporter.ImageExportSettings())
|
||||
if export_result != QgsLayoutExporter.Success:
|
||||
print("Failed to export map!")
|
||||
else:
|
||||
print("Map exported to:", export_path)
|
||||
@@ -0,0 +1,122 @@
|
||||
from osgeo import gdal
|
||||
import numpy as np
|
||||
import sys
|
||||
import os
|
||||
import re
|
||||
import math
|
||||
|
||||
def tif_to_array(path):
|
||||
data = gdal.Open(path)
|
||||
if data is None:
|
||||
raise FileNotFoundError(f"Cannot open file: {path}")
|
||||
band = data.GetRasterBand(1)
|
||||
array = band.ReadAsArray().astype(np.float32)
|
||||
return data, array
|
||||
|
||||
def handle_nodata(array):
|
||||
# Replace only negative values with NaN
|
||||
clean_array = np.where(array < 0, np.nan, array)
|
||||
return clean_array
|
||||
|
||||
def reflectance_calc(array, reflectance_mult, reflectance_add, sun_elevation):
|
||||
# Debugging print statements
|
||||
print(f"Reflectance Mult: {reflectance_mult}, Reflectance Add: {reflectance_add}, Sun Elevation: {sun_elevation}")
|
||||
print(f"Array Sample Before: {array[:5, :5]}") # Print a small portion of the array for inspection
|
||||
|
||||
result = ((array * reflectance_mult) + reflectance_add) / math.sin(math.radians(sun_elevation))
|
||||
|
||||
# Debugging print statements
|
||||
print(f"Array Sample After: {result[:5, :5]}") # Print a small portion of the result for inspection
|
||||
return result
|
||||
|
||||
def save_tif(array, data, output_path):
|
||||
driver = gdal.GetDriverByName('GTiff')
|
||||
rows, cols = array.shape
|
||||
out_data = driver.Create(output_path, cols, rows, 1, gdal.GDT_Float32)
|
||||
out_data.SetGeoTransform(data.GetGeoTransform())
|
||||
out_data.SetProjection(data.GetProjection())
|
||||
out_band = out_data.GetRasterBand(1)
|
||||
out_band.WriteArray(array)
|
||||
out_band.SetNoDataValue(-9999)
|
||||
out_band.FlushCache()
|
||||
out_data = None
|
||||
|
||||
def get_output_path(output_dir, input_path, suffix):
|
||||
basename = os.path.basename(input_path)
|
||||
basename_no_ext = os.path.splitext(basename)[0]
|
||||
# basename_no_ext = re.sub(r'_B[1-9]|_B10|_B11', '', basename_no_ext)
|
||||
path = os.path.join(output_dir, f"{basename_no_ext}_{suffix}.TIF")
|
||||
return path
|
||||
|
||||
def print_min_max(array, label):
|
||||
print(label, " Min:", np.nanmin(array), " Max:", np.nanmax(array))
|
||||
|
||||
def get_reflectance_params(band_number, metadata_lines):
|
||||
reflectance_mult = None
|
||||
reflectance_add = None
|
||||
for line in metadata_lines:
|
||||
if f"REFLECTANCE_MULT_BAND_{band_number}" in line:
|
||||
variable, value = line.split(" = ")
|
||||
reflectance_mult = float(value.strip())
|
||||
if f"REFLECTANCE_ADD_BAND_{band_number}" in line:
|
||||
variable, value = line.split(" = ")
|
||||
reflectance_add = float(value.strip())
|
||||
if reflectance_mult is None or reflectance_add is None:
|
||||
raise ValueError(f"Reflectance parameters not found for band {band_number} in metadata.")
|
||||
return reflectance_mult, reflectance_add
|
||||
|
||||
def process_bands(band_paths, metadata_path, output_dir):
|
||||
# Read metadata
|
||||
with open(metadata_path, "r") as file:
|
||||
metadata_lines = file.readlines()
|
||||
|
||||
# Extract sun elevation
|
||||
sun_elevation = None
|
||||
for line in metadata_lines:
|
||||
if "SUN_ELEVATION" in line:
|
||||
variable, value = line.split(" = ")
|
||||
sun_elevation = float(value.strip())
|
||||
break
|
||||
|
||||
if sun_elevation is None:
|
||||
raise ValueError("Sun elevation not found in metadata.")
|
||||
|
||||
# Debugging sun elevation value
|
||||
print(f"Sun Elevation: {sun_elevation}, Sun Elevation Radians: {math.radians(sun_elevation)}")
|
||||
|
||||
# Process each band
|
||||
for band_path in band_paths:
|
||||
band_number = int(re.search(r'_B(\d+)', band_path).group(1))
|
||||
data, array = tif_to_array(band_path)
|
||||
array = handle_nodata(array)
|
||||
|
||||
# Get reflectance parameters for the band
|
||||
reflectance_mult, reflectance_add = get_reflectance_params(band_number, metadata_lines)
|
||||
|
||||
# Calculate reflectance for the band
|
||||
reflectance = reflectance_calc(array, reflectance_mult, reflectance_add, sun_elevation)
|
||||
|
||||
# Create output directory if it doesn't exist
|
||||
if not os.path.exists(output_dir):
|
||||
os.makedirs(output_dir)
|
||||
|
||||
# Save Top of Atmosphere (TOA) reflectance for the band
|
||||
output_path = get_output_path(output_dir, band_path, f"toa")
|
||||
save_tif(reflectance, data, output_path)
|
||||
|
||||
# Print min and max for the reflectance band
|
||||
print_min_max(reflectance, f"Band {band_number} Reflectance")
|
||||
|
||||
def main():
|
||||
if len(sys.argv) < 4:
|
||||
print("Usage: python3 script.py <metadata_path> <output_directory> <band_paths...>")
|
||||
sys.exit(1)
|
||||
|
||||
metadata_path = sys.argv[1]
|
||||
output_dir = sys.argv[2]
|
||||
band_paths = sys.argv[3:]
|
||||
|
||||
process_bands(band_paths, metadata_path, output_dir)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,107 @@
|
||||
from osgeo import gdal
|
||||
import numpy as np
|
||||
import sys
|
||||
import os
|
||||
import re
|
||||
import math
|
||||
|
||||
def tif_to_array(path):
|
||||
data = gdal.Open(path)
|
||||
if data is None:
|
||||
raise FileNotFoundError(f"Cannot open file: {path}")
|
||||
band = data.GetRasterBand(1)
|
||||
array = band.ReadAsArray().astype(np.float32)
|
||||
return data, array
|
||||
|
||||
def get_radiance_params(band_number, metadata_lines):
|
||||
radiance_mult = None
|
||||
radiance_add = None
|
||||
for line in metadata_lines:
|
||||
if f"RADIANCE_MULT_BAND_{band_number}" in line:
|
||||
_, value = line.split(" = ")
|
||||
radiance_mult = float(value.strip())
|
||||
if f"RADIANCE_ADD_BAND_{band_number}" in line:
|
||||
_, value = line.split(" = ")
|
||||
radiance_add = float(value.strip())
|
||||
if radiance_mult is None or radiance_add is None:
|
||||
raise ValueError(f"Radiance parameters not found for band {band_number} in metadata.")
|
||||
return radiance_mult, radiance_add
|
||||
|
||||
def save_tif(array, data, output_path):
|
||||
driver = gdal.GetDriverByName('GTiff')
|
||||
rows, cols = array.shape
|
||||
out_data = driver.Create(output_path, cols, rows, 1, gdal.GDT_Float32)
|
||||
out_data.SetGeoTransform(data.GetGeoTransform())
|
||||
out_data.SetProjection(data.GetProjection())
|
||||
out_band = out_data.GetRasterBand(1)
|
||||
out_band.WriteArray(array)
|
||||
out_band.SetNoDataValue(-9999)
|
||||
out_band.FlushCache()
|
||||
out_data = None
|
||||
|
||||
def get_output_path(output_dir, input_path, suffix):
|
||||
basename = os.path.basename(input_path)
|
||||
basename_no_ext = os.path.splitext(basename)[0]
|
||||
path = os.path.join(output_dir, f"{basename_no_ext}_{suffix}.TIF")
|
||||
return path
|
||||
|
||||
def print_min_max(array, label):
|
||||
print(label, " Min:", np.nanmin(array), " Max:", np.nanmax(array))
|
||||
|
||||
def process_bands(bands, metadata_path, output_dir):
|
||||
# Read metadata
|
||||
with open(metadata_path, "r") as file:
|
||||
metadata_lines = file.readlines()
|
||||
|
||||
# Extract sun elevation (optional for radiance, included for debugging)
|
||||
sun_elevation = None
|
||||
for line in metadata_lines:
|
||||
if "SUN_ELEVATION" in line:
|
||||
_, value = line.split(" = ")
|
||||
sun_elevation = float(value.strip())
|
||||
break
|
||||
|
||||
# Debugging sun elevation value
|
||||
if sun_elevation is not None:
|
||||
print(f"Sun Elevation: {sun_elevation}, Sun Elevation Radians: {math.radians(sun_elevation)}")
|
||||
|
||||
if not os.path.exists(output_dir):
|
||||
os.makedirs(output_dir)
|
||||
|
||||
# Process each band
|
||||
for band_data in bands:
|
||||
band_number, band_path = band_data[0], band_data[1]
|
||||
|
||||
# Load the band data
|
||||
data, array = tif_to_array(band_path)
|
||||
|
||||
# Get radiance parameters for the band
|
||||
radiance_mult, radiance_add = get_radiance_params(band_number, metadata_lines)
|
||||
|
||||
# Calculate radiance
|
||||
radiance_array = (array * radiance_mult) + radiance_add
|
||||
|
||||
# Save the Top of Atmosphere (TOA) radiance
|
||||
output_path = get_output_path(output_dir, band_path, "toa_radiance")
|
||||
save_tif(radiance_array, data, output_path)
|
||||
|
||||
# Print min and max for the radiance band
|
||||
print_min_max(radiance_array, f"Band {band_number} Radiance")
|
||||
|
||||
def main():
|
||||
print(f"Number of arguments received: {len(sys.argv) - 1}")
|
||||
print("Arguments:", sys.argv)
|
||||
if len(sys.argv) < 4:
|
||||
print("Usage: python3 script.py <metadata_path> <output_directory> <band_paths...>")
|
||||
sys.exit(1)
|
||||
|
||||
metadata_path = sys.argv[1]
|
||||
output_dir = sys.argv[2]
|
||||
band_paths = sys.argv[3:]
|
||||
|
||||
bands = [(int(re.search(r'B(\d+)', path).group(1)), path) for path in band_paths]
|
||||
|
||||
process_bands(bands, metadata_path, output_dir)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,119 @@
|
||||
# Cleanup
|
||||
project = QgsProject.instance()
|
||||
layer_ids = list(project.mapLayers().keys())
|
||||
for layer_id in layer_ids:
|
||||
project.removeMapLayer(layer_id)
|
||||
|
||||
# Loading B10 Layer
|
||||
path_st_b10 = "/home/huaqo/dev/courses/2024_Remote_Sensing/lab2/true_color/true_color.tif"
|
||||
st_b10_layer = QgsRasterLayer(path_st_b10, "True Color Composite")
|
||||
if not st_b10_layer.isValid():
|
||||
print("st_b10 layer failed to load!")
|
||||
else:
|
||||
QgsProject.instance().addMapLayer(st_b10_layer)
|
||||
print("st_b10 Layer loaded!")
|
||||
|
||||
# Styling Layer
|
||||
st_b10_style_path = '/home/huaqo/dev/courses/2024_Remote_Sensing/styles/true_color.qml'
|
||||
if not st_b10_layer.loadNamedStyle(st_b10_style_path):
|
||||
print("Failed to load st_b10 style!")
|
||||
else:
|
||||
print("st_b10 style loaded!")
|
||||
|
||||
# Load layout
|
||||
layout = QgsPrintLayout(project)
|
||||
layout.initializeDefaults()
|
||||
|
||||
# Map
|
||||
map_item = QgsLayoutItemMap(layout)
|
||||
map_item.attemptMove(QgsLayoutPoint(20, 30, QgsUnitTypes.LayoutMillimeters)) # Added space between top and map
|
||||
map_item.attemptResize(QgsLayoutSize(200, 150, QgsUnitTypes.LayoutMillimeters)) # Reduced height for better spacing
|
||||
map_item.zoomToExtent(st_b10_layer.extent())
|
||||
layout.addLayoutItem(map_item)
|
||||
|
||||
# Add Coordinate Grid
|
||||
grid = map_item.grid()
|
||||
grid.setEnabled(True)
|
||||
grid.setIntervalX(100000) # Set interval for grid lines in map units
|
||||
grid.setIntervalY(100000)
|
||||
grid.setAnnotationEnabled(True)
|
||||
grid.setAnnotationPrecision(0) # Set annotation precision for grid labels
|
||||
grid.setFrameStyle(QgsLayoutItemMapGrid.Zebra) # Optional grid style
|
||||
|
||||
# Label
|
||||
title_label = QgsLayoutItemLabel(layout)
|
||||
title_label.setText("True Color Composite - Lab 2")
|
||||
title_label.setFont(QFont("Arial", 16))
|
||||
title_label.setHAlign(Qt.AlignCenter)
|
||||
title_label.attemptMove(QgsLayoutPoint(105, 10, QgsUnitTypes.LayoutMillimeters))
|
||||
title_label.adjustSizeToText()
|
||||
layout.addLayoutItem(title_label)
|
||||
|
||||
# Legend
|
||||
legend_item = QgsLayoutItemLegend(layout)
|
||||
legend_item.setLinkedMap(map_item)
|
||||
legend_item.setTitle("Legend")
|
||||
legend_item.setFrameEnabled(True)
|
||||
legend_item.attemptMove(QgsLayoutPoint(245, 30, QgsUnitTypes.LayoutMillimeters)) # Adjusted for better alignment and spacing
|
||||
layout.addLayoutItem(legend_item)
|
||||
|
||||
# Scalebar
|
||||
scalebar_item = QgsLayoutItemScaleBar(layout)
|
||||
scalebar_item.setStyle('Single Box') # Change to 'Single Box' or 'Double Box' for a proper scalebar
|
||||
scalebar_item.setLinkedMap(map_item)
|
||||
scalebar_item.setUnitLabel('m') # Add unit label to make it informative
|
||||
scalebar_item.setNumberOfSegments(4) # Specify the number of segments in the scalebar
|
||||
scalebar_item.setNumberOfSegmentsLeft(0) # Segments to the left of zero (if any)
|
||||
scalebar_item.setUnitsPerSegment(50000) # Set distance per segment in map units
|
||||
scalebar_item.setFont(QFont('Arial', 10)) # Set font size for better readability
|
||||
scalebar_item.setHeight(5) # Set the height of the scalebar
|
||||
scalebar_item.applyDefaultSize()
|
||||
scalebar_item.attemptMove(QgsLayoutPoint(20, 190, QgsUnitTypes.LayoutMillimeters)) # Moved below the map for better organization
|
||||
layout.addLayoutItem(scalebar_item)
|
||||
|
||||
# North Arrow
|
||||
north_arrow_item = QgsLayoutItemPicture(layout)
|
||||
north_arrow_item.setPicturePath('/home/huaqo/dev/courses/2024_Remote_Sensing/styles/north_arrow.svg')
|
||||
if not north_arrow_item.picturePath():
|
||||
print("North arrow image failed to load!")
|
||||
north_arrow_item.setReferencePoint(QgsLayoutItemPicture.UpperLeft)
|
||||
north_arrow_item.attemptMove(QgsLayoutPoint(30, 40, QgsUnitTypes.LayoutMillimeters)) # Adjusted placement for better visibility
|
||||
north_arrow_item.attemptResize(QgsLayoutSize(15, 15, QgsUnitTypes.LayoutMillimeters)) # Increased size for better visibility
|
||||
layout.addLayoutItem(north_arrow_item)
|
||||
|
||||
# Author
|
||||
info_label = QgsLayoutItemLabel(layout)
|
||||
info_label.setText("Author: Joaquin Gottlebe")
|
||||
info_label.setFont(QFont("Arial", 12))
|
||||
info_label.setHAlign(Qt.AlignLeft)
|
||||
info_label.attemptMove(QgsLayoutPoint(245, 100, QgsUnitTypes.LayoutMillimeters))
|
||||
info_label.adjustSizeToText()
|
||||
layout.addLayoutItem(info_label)
|
||||
|
||||
# Author
|
||||
info_label = QgsLayoutItemLabel(layout)
|
||||
info_label.setText("Date: 16.11.2025")
|
||||
info_label.setFont(QFont("Arial", 12))
|
||||
info_label.setHAlign(Qt.AlignLeft)
|
||||
info_label.attemptMove(QgsLayoutPoint(245, 110, QgsUnitTypes.LayoutMillimeters))
|
||||
info_label.adjustSizeToText()
|
||||
layout.addLayoutItem(info_label)
|
||||
|
||||
# Coordinate System
|
||||
info_label = QgsLayoutItemLabel(layout)
|
||||
info_label.setText("CRS: EPSG 32633")
|
||||
info_label.setFont(QFont("Arial", 12))
|
||||
info_label.setHAlign(Qt.AlignLeft)
|
||||
info_label.attemptMove(QgsLayoutPoint(245, 120, QgsUnitTypes.LayoutMillimeters))
|
||||
info_label.adjustSizeToText()
|
||||
layout.addLayoutItem(info_label)
|
||||
|
||||
|
||||
# Export
|
||||
export_path = "/home/huaqo/dev/courses/2024_Remote_Sensing/lab2/maps/true_color.png"
|
||||
exporter = QgsLayoutExporter(layout)
|
||||
export_result = exporter.exportToImage(export_path, QgsLayoutExporter.ImageExportSettings())
|
||||
if export_result != QgsLayoutExporter.Success:
|
||||
print("Failed to export map!")
|
||||
else:
|
||||
print("Map exported to:", export_path)
|
||||
Reference in New Issue
Block a user