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

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

|
||||
|
||||

|
||||
|
||||
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.
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
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.
|
||||
@@ -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
|
||||
@@ -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()
|
||||
@@ -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()
|
||||
@@ -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()
|
||||
@@ -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()
|
||||
@@ -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()
|
||||
@@ -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()
|
||||
@@ -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()
|
||||
@@ -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()
|
||||
@@ -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()
|
||||
@@ -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()
|
||||
Reference in New Issue
Block a user