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
+27
View File
@@ -0,0 +1,27 @@
# Lab 3
Name: Joaquin Gottlebe
Matrikelnummer: 829101
## Question 1
![](maps/ndwi.png)
## Question 2
![](maps/diff_ndwi.png)
![](plots/diff_hist.png)
Landsat 8 uses broader NIR and Green bands with a 30m resolution, while Sentinel-2 uses narrower bands with a 10m resolution. Differences in atmospheric correction methods and time of capture could also be factors.
## Question 3
For the third question i choose the Chacao Channel as it is also a very interesting region because of its national parks agriculture and abundance of waterbodies.
![](maps/ndvi2.png)
![](maps/ndwi2.png)
Here the symbology was reduced to 0-0.2 because the calculations resultet in a peak around this area and near to zero values till 1. Which doenst make much sense because the area has a lot of waterbodies. I couldnt find and error.
+44
View File
@@ -0,0 +1,44 @@
.PHONY: all preproc1 preproc2 proc proc2 maps export
ACTIVATE_SCRIPT = ~/miniforge3/bin/activate
CONDA_ENV_NAME = gdal_env
PANDOC=pandoc
all: preproc1 preproc2 proc proc2 maps export
preproc1:
gdal_translate -of GTiff /home/huaqo/dev/courses/2024_Remote_Sensing/lab3/S2A_MSIL2A_20170527T102031_N9999_R065_T33UUU_20191015T100203.SAFE/GRANULE/L2A_T33UUU_A010072_20170527T102301/IMG_DATA/R10m/T33UUU_20170527T102031_B02_10m.jp2 /home/huaqo/dev/courses/2024_Remote_Sensing/lab3/sen2tiff/B02.tif && \
gdal_translate -of GTiff /home/huaqo/dev/courses/2024_Remote_Sensing/lab3/S2A_MSIL2A_20170527T102031_N9999_R065_T33UUU_20191015T100203.SAFE/GRANULE/L2A_T33UUU_A010072_20170527T102301/IMG_DATA/R10m/T33UUU_20170527T102031_B03_10m.jp2 /home/huaqo/dev/courses/2024_Remote_Sensing/lab3/sen2tiff/B03.tif && \
gdal_translate -of GTiff /home/huaqo/dev/courses/2024_Remote_Sensing/lab3/S2A_MSIL2A_20170527T102031_N9999_R065_T33UUU_20191015T100203.SAFE/GRANULE/L2A_T33UUU_A010072_20170527T102301/IMG_DATA/R10m/T33UUU_20170527T102031_B04_10m.jp2 /home/huaqo/dev/courses/2024_Remote_Sensing/lab3/sen2tiff/B04.tif && \
gdal_translate -of GTiff /home/huaqo/dev/courses/2024_Remote_Sensing/lab3/S2A_MSIL2A_20170527T102031_N9999_R065_T33UUU_20191015T100203.SAFE/GRANULE/L2A_T33UUU_A010072_20170527T102301/IMG_DATA/R10m/T33UUU_20170527T102031_B08_10m.jp2 /home/huaqo/dev/courses/2024_Remote_Sensing/lab3/sen2tiff/B08.tif
preproc2:
gdal_translate -of GTiff /home/huaqo/dev/courses/2024_Remote_Sensing/lab3/S2A_MSIL2A_20170527T102031_N9999_R065_T33UUU_20191015T100203.SAFE/GRANULE/L2A_T33UUU_A010072_20170527T102301/IMG_DATA/R10m/T33UUU_20170527T102031_B02_10m.jp2 /home/huaqo/dev/courses/2024_Remote_Sensing/lab3/sen2tiff2/B02.tif && \
gdal_translate -of GTiff /home/huaqo/dev/courses/2024_Remote_Sensing/lab3/S2A_MSIL2A_20241118T143741_N0511_R096_T18GXU_20241118T201252.SAFE/GRANULE/L2A_T18GXU_A049142_20241118T144600/IMG_DATA/R10m/T18GXU_20241118T143741_B03_10m.jp2 /home/huaqo/dev/courses/2024_Remote_Sensing/lab3/sen2tiff2/B03.tif && \
gdal_translate -of GTiff /home/huaqo/dev/courses/2024_Remote_Sensing/lab3/S2A_MSIL2A_20241118T143741_N0511_R096_T18GXU_20241118T201252.SAFE/GRANULE/L2A_T18GXU_A049142_20241118T144600/IMG_DATA/R10m/T18GXU_20241118T143741_B04_10m.jp2 /home/huaqo/dev/courses/2024_Remote_Sensing/lab3/sen2tiff2/B04.tif && \
gdal_translate -of GTiff /home/huaqo/dev/courses/2024_Remote_Sensing/lab3/S2A_MSIL2A_20241118T143741_N0511_R096_T18GXU_20241118T201252.SAFE/GRANULE/L2A_T18GXU_A049142_20241118T144600/IMG_DATA/R10m/T18GXU_20241118T143741_B08_10m.jp2 /home/huaqo/dev/courses/2024_Remote_Sensing/lab3/sen2tiff2/B08.tif
proc:
@source $(ACTIVATE_SCRIPT) && conda activate $(CONDA_ENV_NAME) && \
python3 ndvi.py sen2tiff/B04.tif sen2tiff/B08.tif ndvi && \
python3 ndwi.py sen2tiff/B03.tif sen2tiff/B08.tif ndwi && \
python clip.py /home/huaqo/dev/courses/2024_Remote_Sensing/lab3/l8_ndvi/l8_ndvi.TIF /home/huaqo/dev/courses/2024_Remote_Sensing/lab3/ndvi/B04_ndvi.TIF /home/huaqo/dev/courses/2024_Remote_Sensing/lab3/clip/l8_ndvi.TIF /home/huaqo/dev/courses/2024_Remote_Sensing/lab3/clip/sen_ndvi.TIF && \
python diff.py /home/huaqo/dev/courses/2024_Remote_Sensing/lab3/clip/sen_ndvi.TIF /home/huaqo/dev/courses/2024_Remote_Sensing/lab3/clip/l8_ndvi.TIF /home/huaqo/dev/courses/2024_Remote_Sensing/lab3/diff/aligned.TIF /home/huaqo/dev/courses/2024_Remote_Sensing/lab3/diff/diff.TIF && \
python clip.py /home/huaqo/dev/courses/2024_Remote_Sensing/lab3/l8_ndwi/l8_ndwi.TIF /home/huaqo/dev/courses/2024_Remote_Sensing/lab3/ndwi/B03_ndwi.TIF /home/huaqo/dev/courses/2024_Remote_Sensing/lab3/clip/l8_ndwi.TIF /home/huaqo/dev/courses/2024_Remote_Sensing/lab3/clip/sen_ndwi.TIF && \
python diff.py /home/huaqo/dev/courses/2024_Remote_Sensing/lab3/clip/sen_ndwi.TIF /home/huaqo/dev/courses/2024_Remote_Sensing/lab3/clip/l8_ndwi.TIF /home/huaqo/dev/courses/2024_Remote_Sensing/lab3/diff/aligned_ndwi.TIF /home/huaqo/dev/courses/2024_Remote_Sensing/lab3/diff/diff_ndwi.TIF && \
python hist.py diff/diff_ndwi.TIF --output plots/diff_hist.png
proc2:
@source $(ACTIVATE_SCRIPT) && conda activate $(CONDA_ENV_NAME) && \
python3 ndvi.py sen2tiff2/B04.tif sen2tiff2/B08.tif ndvi2 && \
python3 ndwi.py sen2tiff2/B03.tif sen2tiff2/B08.tif ndwi2
maps:
python3 ndvi_map.py
python3 ndvi_map2.py
python3 ndwi_map.py
python3 ndwi_map2.py
python3 diff_map.py
export:
$(PANDOC) Lab3.md -o rcm01_2425_lab3_gottlebe_829101.pdf
+62
View File
@@ -0,0 +1,62 @@
import rasterio
from rasterio.mask import mask
from shapely.geometry import box
import argparse
def get_common_extent(raster1_path, raster2_path):
"""Calculate the common extent (intersection) of two rasters."""
with rasterio.open(raster1_path) as src1, rasterio.open(raster2_path) as src2:
bounds1 = src1.bounds
bounds2 = src2.bounds
# Calculate the intersection of the extents
common_bounds = box(
max(bounds1.left, bounds2.left),
max(bounds1.bottom, bounds2.bottom),
min(bounds1.right, bounds2.right),
min(bounds1.top, bounds2.top),
)
return common_bounds
def clip_raster_to_extent(raster_path, extent, output_path):
"""Clip the raster to the provided extent and save it to a new file."""
with rasterio.open(raster_path) as src:
# Convert extent to GeoJSON-like dict for rasterio.mask
geojson_extent = [extent.__geo_interface__]
# Clip the raster to the extent
out_image, out_transform = mask(src, geojson_extent, crop=True)
out_meta = src.meta.copy()
# Update metadata with new dimensions, transform, and bounds
out_meta.update({
"driver": "GTiff",
"height": out_image.shape[1],
"width": out_image.shape[2],
"transform": out_transform
})
# Save the clipped raster
with rasterio.open(output_path, "w", **out_meta) as dest:
dest.write(out_image)
def main():
parser = argparse.ArgumentParser(description="Clip two rasters to their common extent.")
parser.add_argument("raster1", help="Path to the first raster.")
parser.add_argument("raster2", help="Path to the second raster.")
parser.add_argument("output1", help="Path to save the clipped first raster.")
parser.add_argument("output2", help="Path to save the clipped second raster.")
args = parser.parse_args()
# Determine the common extent
common_extent = get_common_extent(args.raster1, args.raster2)
# Clip both rasters to the common extent
clip_raster_to_extent(args.raster1, common_extent, args.output1)
clip_raster_to_extent(args.raster2, common_extent, args.output2)
print("Rasters clipped successfully!")
if __name__ == "__main__":
main()
+85
View File
@@ -0,0 +1,85 @@
import rasterio
from rasterio.warp import reproject, Resampling
import numpy as np
import argparse
def resample_raster_to_match(reference_path, target_path, output_path):
"""Resample the target raster to match the resolution, extent, and CRS of the reference raster."""
with rasterio.open(reference_path) as ref:
ref_transform = ref.transform
ref_crs = ref.crs
ref_width = ref.width
ref_height = ref.height
ref_nodata = ref.nodata or -9999
with rasterio.open(target_path) as target:
target_nodata = target.nodata or -9999
profile = target.profile
profile.update(
transform=ref_transform,
crs=ref_crs,
width=ref_width,
height=ref_height,
nodata=ref_nodata
)
# Resample target raster
with rasterio.open(output_path, "w", **profile) as output:
for i in range(1, target.count + 1):
reproject(
source=rasterio.band(target, i),
destination=rasterio.band(output, i),
src_transform=target.transform,
src_crs=target.crs,
dst_transform=ref_transform,
dst_crs=ref_crs,
resampling=Resampling.bilinear,
)
def calculate_difference(raster1_path, raster2_resampled_path, output_path):
"""Calculate the difference between two aligned rasters."""
with rasterio.open(raster1_path) as src1:
raster1 = src1.read(1)
nodata1 = src1.nodata or -9999
profile = src1.profile
with rasterio.open(raster2_resampled_path) as src2:
raster2 = src2.read(1)
nodata2 = src2.nodata or -9999
# Replace nodata values with NaN for calculation
raster1 = np.where(raster1 == nodata1, np.nan, raster1)
raster2 = np.where(raster2 == nodata2, np.nan, raster2)
# Compute difference
difference = raster1 - raster2
# Replace NaN with nodata for saving
difference[np.isnan(difference)] = -9999
# Update metadata to include nodata
profile.update(nodata=-9999)
# Save the difference raster
with rasterio.open(output_path, "w", **profile) as dst:
dst.write(difference, 1)
def main():
parser = argparse.ArgumentParser(description="Resample a raster and calculate the difference.")
parser.add_argument("reference_raster", help="Path to the reference raster.")
parser.add_argument("target_raster", help="Path to the target raster to be resampled.")
parser.add_argument("aligned_raster", help="Path to save the resampled (aligned) raster.")
parser.add_argument("difference_raster", help="Path to save the difference raster.")
args = parser.parse_args()
# Resample raster 2 to match raster 1
resample_raster_to_match(args.reference_raster, args.target_raster, args.aligned_raster)
# Calculate the difference
calculate_difference(args.reference_raster, args.aligned_raster, args.difference_raster)
print("Aligned raster and difference raster created successfully!")
if __name__ == "__main__":
main()
+171
View File
@@ -0,0 +1,171 @@
from qgis.core import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *
from PyQt5 import *
from datetime import datetime
import os
QGIS_PREFIX_PATH = "/usr/share/qgis"
LAYER_PATH = "/home/huaqo/dev/courses/2024_Remote_Sensing/lab3/diff/diff_ndwi.TIF"
STYLE_PATH = "/home/huaqo/dev/courses/2024_Remote_Sensing/styles/diff.qml"
EXPORT_PATH = "/home/huaqo/dev/courses/2024_Remote_Sensing/lab3/maps/diff_ndwi.png"
NORTH_ARROW_PATH = "/home/huaqo/dev/courses/2024_Remote_Sensing/styles/north_arrow.svg"
map_pos_x = 20
map_pos_y = 30
map_size_x = 200
map_size_y = 150
grid_interval_x = 100000
grid_interval_y = 100000
LAYER_NAME = "DIFF"
legend_pos_x = 245
legend_pos_y = 30
scalebar_pos_x = 20
scalebar_pos_y = 190
north_arrow_pos_x = 30
north_arrow_pos_y = 40
north_arrow_size_x = 15
north_arrow_size_y = 15
title_text = "Difference NDWI Landsat 8 and Sentinel 2 - Lab 3"
title_fontsize = 16
title_height = 10
author_text = "Author: Joaquin Gottlebe"
author_pos_y = 100
date_text = f"Date: {datetime.now().strftime('%d.%m.%Y')}"
date_pos_y = 110
crs_text = "CRS: EPSG 32633"
crs_pos_y = 120
fontfamily = "Arial"
info_font_size = 12
info_pos_x = legend_pos_x
# Initialize QGIS Application
QgsApplication.setPrefixPath(QGIS_PREFIX_PATH, True)
qgs = QgsApplication([], False)
qgs.initQgis()
# Cleanup
project = QgsProject.instance()
layer_ids = list(project.mapLayers().keys())
for layer_id in layer_ids:
project.removeMapLayer(layer_id)
# Load Layer
raster_layer = QgsRasterLayer(LAYER_PATH, LAYER_NAME)
if not raster_layer.isValid():
raise Exception(f"{LAYER_NAME} layer failed to load!")
QgsProject.instance().addMapLayer(raster_layer)
if not raster_layer.loadNamedStyle(STYLE_PATH):
raise Exception(f"Style {STYLE_PATH} failed to load")
# Create Layout
layout = QgsPrintLayout(project)
layout.initializeDefaults()
# Map
map_item = QgsLayoutItemMap(layout)
map_item.attemptMove(QgsLayoutPoint(map_pos_x,map_pos_y, QgsUnitTypes.LayoutMillimeters))
map_item.attemptResize(QgsLayoutSize(map_size_x, map_size_y, QgsUnitTypes.LayoutMillimeters))
map_item.zoomToExtent(raster_layer.extent())
layout.addLayoutItem(map_item)
# Add Coordinate Grid
grid = map_item.grid()
grid.setEnabled(True)
grid.setIntervalX(grid_interval_x)
grid.setIntervalY(grid_interval_y)
grid.setAnnotationEnabled(True)
grid.setAnnotationPrecision(0)
grid.setFrameStyle(QgsLayoutItemMapGrid.Zebra)
# Label
title_label = QgsLayoutItemLabel(layout)
title_label.setText(title_text)
title_label.setFont(QFont(fontfamily, title_fontsize))
title_label.setHAlign(Qt.AlignCenter)
title_label.adjustSizeToText()
layout_width = layout.pageCollection().page(0).pageSize().width()
title_width = title_label.rectWithFrame().width()
center_x = (layout_width - title_width) / 2
title_label.attemptMove(QgsLayoutPoint(center_x, title_height, QgsUnitTypes.LayoutMillimeters))
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(legend_pos_x,legend_pos_y, QgsUnitTypes.LayoutMillimeters))
layout.addLayoutItem(legend_item)
# Scalebar
scalebar_item = QgsLayoutItemScaleBar(layout)
scalebar_item.setStyle('Single Box')
scalebar_item.setLinkedMap(map_item)
scalebar_item.setUnitLabel('m')
scalebar_item.setNumberOfSegments(4)
scalebar_item.setNumberOfSegmentsLeft(0)
scalebar_item.setUnitsPerSegment(50000)
scalebar_item.applyDefaultSize()
scalebar_item.attemptMove(QgsLayoutPoint(scalebar_pos_x,scalebar_pos_y, QgsUnitTypes.LayoutMillimeters))
layout.addLayoutItem(scalebar_item)
# North Arrow
north_arrow_item = QgsLayoutItemPicture(layout)
north_arrow_item.setPicturePath(NORTH_ARROW_PATH)
if not north_arrow_item.picturePath():
print("North arrow image failed to load!")
north_arrow_item.setReferencePoint(QgsLayoutItemPicture.UpperLeft)
north_arrow_item.attemptMove(QgsLayoutPoint(north_arrow_pos_x,north_arrow_pos_y,QgsUnitTypes.LayoutMillimeters))
north_arrow_item.attemptResize(QgsLayoutSize(north_arrow_size_x,north_arrow_size_y,QgsUnitTypes.LayoutMillimeters))
layout.addLayoutItem(north_arrow_item)
# Author Info
info_label = QgsLayoutItemLabel(layout)
info_label.setText(author_text)
info_label.setFont(QFont(fontfamily, info_font_size))
info_label.setHAlign(Qt.AlignLeft)
info_label.attemptMove(QgsLayoutPoint(info_pos_x, author_pos_y, QgsUnitTypes.LayoutMillimeters))
info_label.adjustSizeToText()
layout.addLayoutItem(info_label)
# Date Info
info_label = QgsLayoutItemLabel(layout)
info_label.setText(date_text)
info_label.setFont(QFont(fontfamily, info_font_size))
info_label.setHAlign(Qt.AlignLeft)
info_label.attemptMove(QgsLayoutPoint(info_pos_x, date_pos_y, QgsUnitTypes.LayoutMillimeters))
info_label.adjustSizeToText()
layout.addLayoutItem(info_label)
# CRS Info
info_label = QgsLayoutItemLabel(layout)
info_label.setText(crs_text)
info_label.setFont(QFont(fontfamily, info_font_size))
info_label.setHAlign(Qt.AlignLeft)
info_label.attemptMove(QgsLayoutPoint(info_pos_x, crs_pos_y, QgsUnitTypes.LayoutMillimeters))
info_label.adjustSizeToText()
layout.addLayoutItem(info_label)
# Export Map
exporter = QgsLayoutExporter(layout)
if os.path.exists(EXPORT_PATH):
os.remove(EXPORT_PATH)
export_result = exporter.exportToImage(EXPORT_PATH, QgsLayoutExporter.ImageExportSettings())
if export_result != QgsLayoutExporter.Success:
print(f"Failed to export {LAYER_NAME} map!")
else:
print(f"Map exported to: {EXPORT_PATH}")
# Cleanup QGIS
qgs.exitQgis()
+46
View File
@@ -0,0 +1,46 @@
import argparse
import rasterio
import numpy as np
import matplotlib.pyplot as plt
def plot_histogram(tiff_path, output_path=None):
"""Plot a linear histogram for a given TIFF file and save it if an output path is provided."""
try:
with rasterio.open(tiff_path) as src:
data = src.read(1) # Read the first band
# Mask nodata values
if src.nodata is not None:
data = np.ma.masked_equal(data, src.nodata)
# Flatten the array to 1D for histogram plotting
data = data.compressed() if np.ma.isMaskedArray(data) else data.flatten()
# Plot the histogram
plt.figure(figsize=(10, 6))
plt.hist(data, bins=256, range=(np.min(data), np.max(data)), edgecolor='black', alpha=0.7)
plt.title(f"Histogram of {tiff_path}")
plt.xlabel("Pixel Value")
plt.ylabel("Frequency")
plt.grid(axis='y', alpha=0.75)
# Save the plot if an output path is specified
if output_path:
plt.savefig(output_path, dpi=300)
print(f"Histogram saved to {output_path}")
else:
plt.show()
except Exception as e:
print(f"Error processing file {tiff_path}: {e}")
def main():
parser = argparse.ArgumentParser(description="Plot a histogram for a TIFF file.")
parser.add_argument("tiff_path", help="Path to the TIFF file.")
parser.add_argument("--output", help="Path to save the histogram plot (optional).", default=None)
args = parser.parse_args()
plot_histogram(args.tiff_path, args.output)
if __name__ == "__main__":
main()
+77
View File
@@ -0,0 +1,77 @@
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):
# Normalize the reflectance values to [0, 1]
normalized_array = array / 10000.0
# Replace out-of-range values with NaN
clean_array = np.where((normalized_array < 0) | (normalized_array > 1), np.nan, normalized_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()
+171
View File
@@ -0,0 +1,171 @@
from qgis.core import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *
from PyQt5 import *
from datetime import datetime
import os
QGIS_PREFIX_PATH = "/usr/share/qgis"
LAYER_PATH = "/home/huaqo/dev/courses/2024_Remote_Sensing/lab3/ndvi/B04_ndvi.TIF"
STYLE_PATH = "/home/huaqo/dev/courses/2024_Remote_Sensing/styles/ndvi.qml"
EXPORT_PATH = "/home/huaqo/dev/courses/2024_Remote_Sensing/lab3/maps/ndvi.png"
NORTH_ARROW_PATH = "/home/huaqo/dev/courses/2024_Remote_Sensing/styles/north_arrow.svg"
map_pos_x = 20
map_pos_y = 30
map_size_x = 200
map_size_y = 150
grid_interval_x = 100000
grid_interval_y = 100000
LAYER_NAME = "NDVI"
legend_pos_x = 245
legend_pos_y = 30
scalebar_pos_x = 20
scalebar_pos_y = 190
north_arrow_pos_x = 30
north_arrow_pos_y = 40
north_arrow_size_x = 15
north_arrow_size_y = 15
title_text = "NDVI - Lab 3"
title_fontsize = 16
title_height = 10
author_text = "Author: Joaquin Gottlebe"
author_pos_y = 100
date_text = f"Date: {datetime.now().strftime('%d.%m.%Y')}"
date_pos_y = 110
crs_text = "CRS: EPSG 32633"
crs_pos_y = 120
fontfamily = "Arial"
info_font_size = 12
info_pos_x = legend_pos_x
# Initialize QGIS Application
QgsApplication.setPrefixPath(QGIS_PREFIX_PATH, True)
qgs = QgsApplication([], False)
qgs.initQgis()
# Cleanup
project = QgsProject.instance()
layer_ids = list(project.mapLayers().keys())
for layer_id in layer_ids:
project.removeMapLayer(layer_id)
# Load Layer
raster_layer = QgsRasterLayer(LAYER_PATH, LAYER_NAME)
if not raster_layer.isValid():
raise Exception(f"{LAYER_NAME} layer failed to load!")
QgsProject.instance().addMapLayer(raster_layer)
if not raster_layer.loadNamedStyle(STYLE_PATH):
raise Exception(f"Style {STYLE_PATH} failed to load")
# Create Layout
layout = QgsPrintLayout(project)
layout.initializeDefaults()
# Map
map_item = QgsLayoutItemMap(layout)
map_item.attemptMove(QgsLayoutPoint(map_pos_x,map_pos_y, QgsUnitTypes.LayoutMillimeters))
map_item.attemptResize(QgsLayoutSize(map_size_x, map_size_y, QgsUnitTypes.LayoutMillimeters))
map_item.zoomToExtent(raster_layer.extent())
layout.addLayoutItem(map_item)
# Add Coordinate Grid
grid = map_item.grid()
grid.setEnabled(True)
grid.setIntervalX(grid_interval_x)
grid.setIntervalY(grid_interval_y)
grid.setAnnotationEnabled(True)
grid.setAnnotationPrecision(0)
grid.setFrameStyle(QgsLayoutItemMapGrid.Zebra)
# Label
title_label = QgsLayoutItemLabel(layout)
title_label.setText(title_text)
title_label.setFont(QFont(fontfamily, title_fontsize))
title_label.setHAlign(Qt.AlignCenter)
title_label.adjustSizeToText()
layout_width = layout.pageCollection().page(0).pageSize().width()
title_width = title_label.rectWithFrame().width()
center_x = (layout_width - title_width) / 2
title_label.attemptMove(QgsLayoutPoint(center_x, title_height, QgsUnitTypes.LayoutMillimeters))
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(legend_pos_x,legend_pos_y, QgsUnitTypes.LayoutMillimeters))
layout.addLayoutItem(legend_item)
# Scalebar
scalebar_item = QgsLayoutItemScaleBar(layout)
scalebar_item.setStyle('Single Box')
scalebar_item.setLinkedMap(map_item)
scalebar_item.setUnitLabel('m')
scalebar_item.setNumberOfSegments(4)
scalebar_item.setNumberOfSegmentsLeft(0)
scalebar_item.setUnitsPerSegment(50000)
scalebar_item.applyDefaultSize()
scalebar_item.attemptMove(QgsLayoutPoint(scalebar_pos_x,scalebar_pos_y, QgsUnitTypes.LayoutMillimeters))
layout.addLayoutItem(scalebar_item)
# North Arrow
north_arrow_item = QgsLayoutItemPicture(layout)
north_arrow_item.setPicturePath(NORTH_ARROW_PATH)
if not north_arrow_item.picturePath():
print("North arrow image failed to load!")
north_arrow_item.setReferencePoint(QgsLayoutItemPicture.UpperLeft)
north_arrow_item.attemptMove(QgsLayoutPoint(north_arrow_pos_x,north_arrow_pos_y,QgsUnitTypes.LayoutMillimeters))
north_arrow_item.attemptResize(QgsLayoutSize(north_arrow_size_x,north_arrow_size_y,QgsUnitTypes.LayoutMillimeters))
layout.addLayoutItem(north_arrow_item)
# Author Info
info_label = QgsLayoutItemLabel(layout)
info_label.setText(author_text)
info_label.setFont(QFont(fontfamily, info_font_size))
info_label.setHAlign(Qt.AlignLeft)
info_label.attemptMove(QgsLayoutPoint(info_pos_x, author_pos_y, QgsUnitTypes.LayoutMillimeters))
info_label.adjustSizeToText()
layout.addLayoutItem(info_label)
# Date Info
info_label = QgsLayoutItemLabel(layout)
info_label.setText(date_text)
info_label.setFont(QFont(fontfamily, info_font_size))
info_label.setHAlign(Qt.AlignLeft)
info_label.attemptMove(QgsLayoutPoint(info_pos_x, date_pos_y, QgsUnitTypes.LayoutMillimeters))
info_label.adjustSizeToText()
layout.addLayoutItem(info_label)
# CRS Info
info_label = QgsLayoutItemLabel(layout)
info_label.setText(crs_text)
info_label.setFont(QFont(fontfamily, info_font_size))
info_label.setHAlign(Qt.AlignLeft)
info_label.attemptMove(QgsLayoutPoint(info_pos_x, crs_pos_y, QgsUnitTypes.LayoutMillimeters))
info_label.adjustSizeToText()
layout.addLayoutItem(info_label)
# Export Map
exporter = QgsLayoutExporter(layout)
if os.path.exists(EXPORT_PATH):
os.remove(EXPORT_PATH)
export_result = exporter.exportToImage(EXPORT_PATH, QgsLayoutExporter.ImageExportSettings())
if export_result != QgsLayoutExporter.Success:
print(f"Failed to export {LAYER_NAME} map!")
else:
print(f"Map exported to: {EXPORT_PATH}")
# Cleanup QGIS
qgs.exitQgis()
+171
View File
@@ -0,0 +1,171 @@
from qgis.core import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *
from PyQt5 import *
from datetime import datetime
import os
QGIS_PREFIX_PATH = "/usr/share/qgis"
LAYER_PATH = "/home/huaqo/dev/courses/2024_Remote_Sensing/lab3/ndvi2/B04_ndvi.TIF"
STYLE_PATH = "/home/huaqo/dev/courses/2024_Remote_Sensing/styles/ndvi2.qml"
EXPORT_PATH = "/home/huaqo/dev/courses/2024_Remote_Sensing/lab3/maps/ndvi2.png"
NORTH_ARROW_PATH = "/home/huaqo/dev/courses/2024_Remote_Sensing/styles/north_arrow.svg"
map_pos_x = 20
map_pos_y = 30
map_size_x = 200
map_size_y = 150
grid_interval_x = 100000
grid_interval_y = 100000
LAYER_NAME = "NDVI"
legend_pos_x = 245
legend_pos_y = 30
scalebar_pos_x = 20
scalebar_pos_y = 190
north_arrow_pos_x = 30
north_arrow_pos_y = 40
north_arrow_size_x = 15
north_arrow_size_y = 15
title_text = "Chacao Channel 18.11.24 NDVI - Lab 3"
title_fontsize = 16
title_height = 10
author_text = "Author: Joaquin Gottlebe"
author_pos_y = 100
date_text = f"Date: {datetime.now().strftime('%d.%m.%Y')}"
date_pos_y = 110
crs_text = "CRS: EPSG 32633"
crs_pos_y = 120
fontfamily = "Arial"
info_font_size = 12
info_pos_x = legend_pos_x
# Initialize QGIS Application
QgsApplication.setPrefixPath(QGIS_PREFIX_PATH, True)
qgs = QgsApplication([], False)
qgs.initQgis()
# Cleanup
project = QgsProject.instance()
layer_ids = list(project.mapLayers().keys())
for layer_id in layer_ids:
project.removeMapLayer(layer_id)
# Load Layer
raster_layer = QgsRasterLayer(LAYER_PATH, LAYER_NAME)
if not raster_layer.isValid():
raise Exception(f"{LAYER_NAME} layer failed to load!")
QgsProject.instance().addMapLayer(raster_layer)
if not raster_layer.loadNamedStyle(STYLE_PATH):
raise Exception(f"Style {STYLE_PATH} failed to load")
# Create Layout
layout = QgsPrintLayout(project)
layout.initializeDefaults()
# Map
map_item = QgsLayoutItemMap(layout)
map_item.attemptMove(QgsLayoutPoint(map_pos_x,map_pos_y, QgsUnitTypes.LayoutMillimeters))
map_item.attemptResize(QgsLayoutSize(map_size_x, map_size_y, QgsUnitTypes.LayoutMillimeters))
map_item.zoomToExtent(raster_layer.extent())
layout.addLayoutItem(map_item)
# Add Coordinate Grid
grid = map_item.grid()
grid.setEnabled(True)
grid.setIntervalX(grid_interval_x)
grid.setIntervalY(grid_interval_y)
grid.setAnnotationEnabled(True)
grid.setAnnotationPrecision(0)
grid.setFrameStyle(QgsLayoutItemMapGrid.Zebra)
# Label
title_label = QgsLayoutItemLabel(layout)
title_label.setText(title_text)
title_label.setFont(QFont(fontfamily, title_fontsize))
title_label.setHAlign(Qt.AlignCenter)
title_label.adjustSizeToText()
layout_width = layout.pageCollection().page(0).pageSize().width()
title_width = title_label.rectWithFrame().width()
center_x = (layout_width - title_width) / 2
title_label.attemptMove(QgsLayoutPoint(center_x, title_height, QgsUnitTypes.LayoutMillimeters))
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(legend_pos_x,legend_pos_y, QgsUnitTypes.LayoutMillimeters))
layout.addLayoutItem(legend_item)
# Scalebar
scalebar_item = QgsLayoutItemScaleBar(layout)
scalebar_item.setStyle('Single Box')
scalebar_item.setLinkedMap(map_item)
scalebar_item.setUnitLabel('m')
scalebar_item.setNumberOfSegments(4)
scalebar_item.setNumberOfSegmentsLeft(0)
scalebar_item.setUnitsPerSegment(50000)
scalebar_item.applyDefaultSize()
scalebar_item.attemptMove(QgsLayoutPoint(scalebar_pos_x,scalebar_pos_y, QgsUnitTypes.LayoutMillimeters))
layout.addLayoutItem(scalebar_item)
# North Arrow
north_arrow_item = QgsLayoutItemPicture(layout)
north_arrow_item.setPicturePath(NORTH_ARROW_PATH)
if not north_arrow_item.picturePath():
print("North arrow image failed to load!")
north_arrow_item.setReferencePoint(QgsLayoutItemPicture.UpperLeft)
north_arrow_item.attemptMove(QgsLayoutPoint(north_arrow_pos_x,north_arrow_pos_y,QgsUnitTypes.LayoutMillimeters))
north_arrow_item.attemptResize(QgsLayoutSize(north_arrow_size_x,north_arrow_size_y,QgsUnitTypes.LayoutMillimeters))
layout.addLayoutItem(north_arrow_item)
# Author Info
info_label = QgsLayoutItemLabel(layout)
info_label.setText(author_text)
info_label.setFont(QFont(fontfamily, info_font_size))
info_label.setHAlign(Qt.AlignLeft)
info_label.attemptMove(QgsLayoutPoint(info_pos_x, author_pos_y, QgsUnitTypes.LayoutMillimeters))
info_label.adjustSizeToText()
layout.addLayoutItem(info_label)
# Date Info
info_label = QgsLayoutItemLabel(layout)
info_label.setText(date_text)
info_label.setFont(QFont(fontfamily, info_font_size))
info_label.setHAlign(Qt.AlignLeft)
info_label.attemptMove(QgsLayoutPoint(info_pos_x, date_pos_y, QgsUnitTypes.LayoutMillimeters))
info_label.adjustSizeToText()
layout.addLayoutItem(info_label)
# CRS Info
info_label = QgsLayoutItemLabel(layout)
info_label.setText(crs_text)
info_label.setFont(QFont(fontfamily, info_font_size))
info_label.setHAlign(Qt.AlignLeft)
info_label.attemptMove(QgsLayoutPoint(info_pos_x, crs_pos_y, QgsUnitTypes.LayoutMillimeters))
info_label.adjustSizeToText()
layout.addLayoutItem(info_label)
# Export Map
exporter = QgsLayoutExporter(layout)
if os.path.exists(EXPORT_PATH):
os.remove(EXPORT_PATH)
export_result = exporter.exportToImage(EXPORT_PATH, QgsLayoutExporter.ImageExportSettings())
if export_result != QgsLayoutExporter.Success:
print(f"Failed to export {LAYER_NAME} map!")
else:
print(f"Map exported to: {EXPORT_PATH}")
# Cleanup QGIS
qgs.exitQgis()
+84
View File
@@ -0,0 +1,84 @@
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):
# Normalize the reflectance values to [0, 1]
normalized_array = array / 10000.0
# Replace out-of-range values with NaN
clean_array = np.where((normalized_array < 0) | (normalized_array > 1), np.nan, normalized_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()
+171
View File
@@ -0,0 +1,171 @@
from qgis.core import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *
from PyQt5 import *
from datetime import datetime
import os
QGIS_PREFIX_PATH = "/usr/share/qgis"
LAYER_PATH = "/home/huaqo/dev/courses/2024_Remote_Sensing/lab3/ndwi/B03_ndwi.TIF"
STYLE_PATH = "/home/huaqo/dev/courses/2024_Remote_Sensing/styles/ndwi.qml"
EXPORT_PATH = "/home/huaqo/dev/courses/2024_Remote_Sensing/lab3/maps/ndwi.png"
NORTH_ARROW_PATH = "/home/huaqo/dev/courses/2024_Remote_Sensing/styles/north_arrow.svg"
map_pos_x = 20
map_pos_y = 30
map_size_x = 200
map_size_y = 150
grid_interval_x = 100000
grid_interval_y = 100000
LAYER_NAME = "NDWI"
legend_pos_x = 245
legend_pos_y = 30
scalebar_pos_x = 20
scalebar_pos_y = 190
north_arrow_pos_x = 30
north_arrow_pos_y = 40
north_arrow_size_x = 15
north_arrow_size_y = 15
title_text = "NDWI - Lab 3"
title_fontsize = 16
title_height = 10
author_text = "Author: Joaquin Gottlebe"
author_pos_y = 100
date_text = f"Date: {datetime.now().strftime('%d.%m.%Y')}"
date_pos_y = 110
crs_text = "CRS: EPSG 32633"
crs_pos_y = 120
info_font_size = 12
info_pos_x = legend_pos_x
fontfamily = "Arial"
# Initialize QGIS Application
QgsApplication.setPrefixPath(QGIS_PREFIX_PATH, True)
qgs = QgsApplication([], False)
qgs.initQgis()
# Cleanup
project = QgsProject.instance()
layer_ids = list(project.mapLayers().keys())
for layer_id in layer_ids:
project.removeMapLayer(layer_id)
# Load Layer
raster_layer = QgsRasterLayer(LAYER_PATH, LAYER_NAME)
if not raster_layer.isValid():
raise Exception(f"{LAYER_NAME} layer failed to load!")
QgsProject.instance().addMapLayer(raster_layer)
if not raster_layer.loadNamedStyle(STYLE_PATH):
raise Exception(f"Style {STYLE_PATH} failed to load")
# Create Layout
layout = QgsPrintLayout(project)
layout.initializeDefaults()
# Map
map_item = QgsLayoutItemMap(layout)
map_item.attemptMove(QgsLayoutPoint(map_pos_x,map_pos_y, QgsUnitTypes.LayoutMillimeters))
map_item.attemptResize(QgsLayoutSize(map_size_x, map_size_y, QgsUnitTypes.LayoutMillimeters))
map_item.zoomToExtent(raster_layer.extent())
layout.addLayoutItem(map_item)
# Add Coordinate Grid
grid = map_item.grid()
grid.setEnabled(True)
grid.setIntervalX(grid_interval_x)
grid.setIntervalY(grid_interval_y)
grid.setAnnotationEnabled(True)
grid.setAnnotationPrecision(0)
grid.setFrameStyle(QgsLayoutItemMapGrid.Zebra)
# Label
title_label = QgsLayoutItemLabel(layout)
title_label.setText(title_text)
title_label.setFont(QFont(fontfamily, title_fontsize))
title_label.setHAlign(Qt.AlignCenter)
title_label.adjustSizeToText()
layout_width = layout.pageCollection().page(0).pageSize().width()
title_width = title_label.rectWithFrame().width()
center_x = (layout_width - title_width) / 2
title_label.attemptMove(QgsLayoutPoint(center_x, title_height, QgsUnitTypes.LayoutMillimeters))
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(legend_pos_x,legend_pos_y, QgsUnitTypes.LayoutMillimeters))
layout.addLayoutItem(legend_item)
# Scalebar
scalebar_item = QgsLayoutItemScaleBar(layout)
scalebar_item.setStyle('Single Box')
scalebar_item.setLinkedMap(map_item)
scalebar_item.setUnitLabel('m')
scalebar_item.setNumberOfSegments(4)
scalebar_item.setNumberOfSegmentsLeft(0)
scalebar_item.setUnitsPerSegment(50000)
scalebar_item.applyDefaultSize()
scalebar_item.attemptMove(QgsLayoutPoint(scalebar_pos_x,scalebar_pos_y, QgsUnitTypes.LayoutMillimeters))
layout.addLayoutItem(scalebar_item)
# North Arrow
north_arrow_item = QgsLayoutItemPicture(layout)
north_arrow_item.setPicturePath(NORTH_ARROW_PATH)
if not north_arrow_item.picturePath():
print("North arrow image failed to load!")
north_arrow_item.setReferencePoint(QgsLayoutItemPicture.UpperLeft)
north_arrow_item.attemptMove(QgsLayoutPoint(north_arrow_pos_x,north_arrow_pos_y,QgsUnitTypes.LayoutMillimeters))
north_arrow_item.attemptResize(QgsLayoutSize(north_arrow_size_x,north_arrow_size_y,QgsUnitTypes.LayoutMillimeters))
layout.addLayoutItem(north_arrow_item)
# Author Info
info_label = QgsLayoutItemLabel(layout)
info_label.setText(author_text)
info_label.setFont(QFont(fontfamily, info_font_size))
info_label.setHAlign(Qt.AlignLeft)
info_label.attemptMove(QgsLayoutPoint(info_pos_x, author_pos_y, QgsUnitTypes.LayoutMillimeters))
info_label.adjustSizeToText()
layout.addLayoutItem(info_label)
# Date Info
info_label = QgsLayoutItemLabel(layout)
info_label.setText(date_text)
info_label.setFont(QFont(fontfamily, info_font_size))
info_label.setHAlign(Qt.AlignLeft)
info_label.attemptMove(QgsLayoutPoint(info_pos_x, date_pos_y, QgsUnitTypes.LayoutMillimeters))
info_label.adjustSizeToText()
layout.addLayoutItem(info_label)
# CRS Info
info_label = QgsLayoutItemLabel(layout)
info_label.setText(crs_text)
info_label.setFont(QFont(fontfamily, info_font_size))
info_label.setHAlign(Qt.AlignLeft)
info_label.attemptMove(QgsLayoutPoint(info_pos_x, crs_pos_y, QgsUnitTypes.LayoutMillimeters))
info_label.adjustSizeToText()
layout.addLayoutItem(info_label)
# Export Map
exporter = QgsLayoutExporter(layout)
if os.path.exists(EXPORT_PATH):
os.remove(EXPORT_PATH)
export_result = exporter.exportToImage(EXPORT_PATH, QgsLayoutExporter.ImageExportSettings())
if export_result != QgsLayoutExporter.Success:
print(f"Failed to export {LAYER_NAME} map!")
else:
print(f"Map exported to: {EXPORT_PATH}")
# Cleanup QGIS
qgs.exitQgis()
+171
View File
@@ -0,0 +1,171 @@
from qgis.core import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *
from PyQt5 import *
from datetime import datetime
import os
QGIS_PREFIX_PATH = "/usr/share/qgis"
LAYER_PATH = "/home/huaqo/dev/courses/2024_Remote_Sensing/lab3/ndwi2/B03_ndwi.TIF"
STYLE_PATH = "/home/huaqo/dev/courses/2024_Remote_Sensing/styles/ndwi2.qml"
EXPORT_PATH = "/home/huaqo/dev/courses/2024_Remote_Sensing/lab3/maps/ndwi2.png"
NORTH_ARROW_PATH = "/home/huaqo/dev/courses/2024_Remote_Sensing/styles/north_arrow.svg"
map_pos_x = 20
map_pos_y = 30
map_size_x = 200
map_size_y = 150
grid_interval_x = 100000
grid_interval_y = 100000
LAYER_NAME = "NDWI"
legend_pos_x = 245
legend_pos_y = 30
scalebar_pos_x = 20
scalebar_pos_y = 190
north_arrow_pos_x = 30
north_arrow_pos_y = 40
north_arrow_size_x = 15
north_arrow_size_y = 15
title_text = "Chacao Channel 18.11.24 NDWI - Lab 3"
title_fontsize = 16
title_height = 10
author_text = "Author: Joaquin Gottlebe"
author_pos_y = 100
date_text = f"Date: {datetime.now().strftime('%d.%m.%Y')}"
date_pos_y = 110
crs_text = "CRS: EPSG 32633"
crs_pos_y = 120
info_font_size = 12
info_pos_x = legend_pos_x
fontfamily = "Arial"
# Initialize QGIS Application
QgsApplication.setPrefixPath(QGIS_PREFIX_PATH, True)
qgs = QgsApplication([], False)
qgs.initQgis()
# Cleanup
project = QgsProject.instance()
layer_ids = list(project.mapLayers().keys())
for layer_id in layer_ids:
project.removeMapLayer(layer_id)
# Load Layer
raster_layer = QgsRasterLayer(LAYER_PATH, LAYER_NAME)
if not raster_layer.isValid():
raise Exception(f"{LAYER_NAME} layer failed to load!")
QgsProject.instance().addMapLayer(raster_layer)
if not raster_layer.loadNamedStyle(STYLE_PATH):
raise Exception(f"Style {STYLE_PATH} failed to load")
# Create Layout
layout = QgsPrintLayout(project)
layout.initializeDefaults()
# Map
map_item = QgsLayoutItemMap(layout)
map_item.attemptMove(QgsLayoutPoint(map_pos_x,map_pos_y, QgsUnitTypes.LayoutMillimeters))
map_item.attemptResize(QgsLayoutSize(map_size_x, map_size_y, QgsUnitTypes.LayoutMillimeters))
map_item.zoomToExtent(raster_layer.extent())
layout.addLayoutItem(map_item)
# Add Coordinate Grid
grid = map_item.grid()
grid.setEnabled(True)
grid.setIntervalX(grid_interval_x)
grid.setIntervalY(grid_interval_y)
grid.setAnnotationEnabled(True)
grid.setAnnotationPrecision(0)
grid.setFrameStyle(QgsLayoutItemMapGrid.Zebra)
# Label
title_label = QgsLayoutItemLabel(layout)
title_label.setText(title_text)
title_label.setFont(QFont(fontfamily, title_fontsize))
title_label.setHAlign(Qt.AlignCenter)
title_label.adjustSizeToText()
layout_width = layout.pageCollection().page(0).pageSize().width()
title_width = title_label.rectWithFrame().width()
center_x = (layout_width - title_width) / 2
title_label.attemptMove(QgsLayoutPoint(center_x, title_height, QgsUnitTypes.LayoutMillimeters))
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(legend_pos_x,legend_pos_y, QgsUnitTypes.LayoutMillimeters))
layout.addLayoutItem(legend_item)
# Scalebar
scalebar_item = QgsLayoutItemScaleBar(layout)
scalebar_item.setStyle('Single Box')
scalebar_item.setLinkedMap(map_item)
scalebar_item.setUnitLabel('m')
scalebar_item.setNumberOfSegments(4)
scalebar_item.setNumberOfSegmentsLeft(0)
scalebar_item.setUnitsPerSegment(50000)
scalebar_item.applyDefaultSize()
scalebar_item.attemptMove(QgsLayoutPoint(scalebar_pos_x,scalebar_pos_y, QgsUnitTypes.LayoutMillimeters))
layout.addLayoutItem(scalebar_item)
# North Arrow
north_arrow_item = QgsLayoutItemPicture(layout)
north_arrow_item.setPicturePath(NORTH_ARROW_PATH)
if not north_arrow_item.picturePath():
print("North arrow image failed to load!")
north_arrow_item.setReferencePoint(QgsLayoutItemPicture.UpperLeft)
north_arrow_item.attemptMove(QgsLayoutPoint(north_arrow_pos_x,north_arrow_pos_y,QgsUnitTypes.LayoutMillimeters))
north_arrow_item.attemptResize(QgsLayoutSize(north_arrow_size_x,north_arrow_size_y,QgsUnitTypes.LayoutMillimeters))
layout.addLayoutItem(north_arrow_item)
# Author Info
info_label = QgsLayoutItemLabel(layout)
info_label.setText(author_text)
info_label.setFont(QFont(fontfamily, info_font_size))
info_label.setHAlign(Qt.AlignLeft)
info_label.attemptMove(QgsLayoutPoint(info_pos_x, author_pos_y, QgsUnitTypes.LayoutMillimeters))
info_label.adjustSizeToText()
layout.addLayoutItem(info_label)
# Date Info
info_label = QgsLayoutItemLabel(layout)
info_label.setText(date_text)
info_label.setFont(QFont(fontfamily, info_font_size))
info_label.setHAlign(Qt.AlignLeft)
info_label.attemptMove(QgsLayoutPoint(info_pos_x, date_pos_y, QgsUnitTypes.LayoutMillimeters))
info_label.adjustSizeToText()
layout.addLayoutItem(info_label)
# CRS Info
info_label = QgsLayoutItemLabel(layout)
info_label.setText(crs_text)
info_label.setFont(QFont(fontfamily, info_font_size))
info_label.setHAlign(Qt.AlignLeft)
info_label.attemptMove(QgsLayoutPoint(info_pos_x, crs_pos_y, QgsUnitTypes.LayoutMillimeters))
info_label.adjustSizeToText()
layout.addLayoutItem(info_label)
# Export Map
exporter = QgsLayoutExporter(layout)
if os.path.exists(EXPORT_PATH):
os.remove(EXPORT_PATH)
export_result = exporter.exportToImage(EXPORT_PATH, QgsLayoutExporter.ImageExportSettings())
if export_result != QgsLayoutExporter.Success:
print(f"Failed to export {LAYER_NAME} map!")
else:
print(f"Map exported to: {EXPORT_PATH}")
# Cleanup QGIS
qgs.exitQgis()