This commit is contained in:
2024-11-28 11:27:09 +01:00
parent d6f5f0cac9
commit aa2ff99dcf
53 changed files with 4366 additions and 27 deletions
+31
View File
@@ -0,0 +1,31 @@
# Lab Assignment 2
Name: Joaquin Gottlebe
Matrikelnummer: 829101
## Question 1
![](maps/ndvi_ndwi.png)
## Question 2
![](maps/surface_temp_b10.png)
![](maps/surface_temp_b11.png)
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
![](maps/true_color.png)
![](maps/false_color.png)
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
![](maps/ndvi_chiloe_2013.png)
![](maps/ndvi_chiloe_2024.png)
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.
+23
View File
@@ -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
+119
View File
@@ -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)
+2
View File
@@ -0,0 +1,2 @@
mktextfm ecrm1000
mktextfm ecrm1000
+73
View File
@@ -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()
+119
View File
@@ -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)
+139
View File
@@ -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)
+81
View File
@@ -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)
+122
View File
@@ -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()
+107
View File
@@ -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()
+119
View File
@@ -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)