BinaryFileHandler.py — UNF-Dateiverarbeitung¶
Quelldatei
Liest die binären WaterGAP-UNF-Rasterdateien (Big-Endian, Formate UNF0/1/2/4) und konvertiert sie in NumPy-Arrays. Enthält außerdem die Pfadverkettungsfunktion für die Eingabedateien.
Extrahiert Dateiinformationen und Metadaten aus dem UNF-Dateinamen.
Diese Funktion bestimmt aus dem Dateinamen die Anzahl der Layer,
die Elementgröße und den Dateityp.
Parameter
| Parameter | Typ | Beschreibung |
|---|---|---|
filepath |
str |
Vollständiger Pfad zur UNF-Binärdatei |
Rückgabe
nlayers : int Anzahl der Layer/Schichten Size : int Größe eines Datenelementes in Bytes Type : str Dateityp als String (z.B. UNF0, UNF1, UNF2, UNF4)
Notizen
- Der Dateiname wird analysiert, um die Layer-Anzahl zu extrahieren.
Beispiel: ".12." bedeutet 12 Layer, ".31." bedeutet 31 Layer.
- Wenn die Layer-Anzahl nicht explizit angegeben ist (z.B. .UNF1),
wird angenommen, dass die Datei nur eine Schicht enthält.
- Die Elementgröße hängt vom Datentyp ab: UNF0/UNF4 = 4 Bytes,
UNF1 = 1 Byte, UNF2 = 2 Bytes.
Extrahiert den Dateinamen einschließlich der Dateiendung aus dem vollständigen Pfad
Bestimmt die Anzahl der Schichten (Layer) aus dem Dateinamen
after_first_dot = filename.split(".")[1]
if after_first_dot.startswith("U"):
nlayers = 1
else:
nlayers = int(after_first_dot)
Bestimmt die Größe eines einzelnen Datenelementes in Bytes basierend auf dem Dateityp
file_extension = filename.split(".")[-1]
if file_extension[-1] == "0":
Size = 4
elif file_extension[-1] == "1":
Size = 1
elif file_extension[-1] == "2":
Size = 2
elif file_extension[-1] == "4":
Size = 4
else:
print("The file is not an UNF type of file.")
Speichert den Dateityp als String (z.B. UNF0, UNF1, UNF2 oder UNF4)
Type = str(file_extension)
return nlayers, Size, Type
import struct
def ReadBin(filepath: str, ng: int) -> list:
Liest Binärdaten aus einer UNF-Datei mit Typkonvertierung.
Liest Binärdaten aus einer UNF-Datei und konvertiert diese je nach
Dateityp in entsprechende Python-Objekte (Float oder Integer).
Parameter
| Parameter | Typ | Beschreibung |
|---|---|---|
filepath |
str |
Pfad zur UNF-Binärdatei ng : int Anzahl der pro Layer zu lesenden Zellen (nur Landflächen) |
Rückgabe
list Liste der konvertierten Datenwerte
Notizen
- UNF0: 32-Bit Gleitkommazahlen im Big-Endian-Format
- UNF1: 8-Bit-Ganzzahlen
- UNF2: 16-Bit-Ganzzahlen
- UNF4: 32-Bit-Ganzzahlen
Type = getFileInfo(filepath)[2]
nbytes = getFileInfo(filepath)[1]
nlayers = getFileInfo(filepath)[0]
data = []
with open(filepath, "rb") as file:
for i in range(ng*nlayers):
try:
b = file.read(nbytes)
Konvertierung je nach Dateityp
if Type == "UNF0":
data.append(struct.unpack('>f', b)[0])
elif Type == "UNF1":
data.append(int.from_bytes(b , byteorder = "big"))
elif Type == "UNF2":
data.append(int.from_bytes(b , byteorder = "big"))
elif Type == "UNF4":
data.append(int.from_bytes(b , byteorder = "big"))
else:
data.append(int.from_bytes(b , byteorder = "big"))
except EOFError:
break
return data
import numpy as np
def FileToArray(file_path: str, GC_path: str, GR_path: str, continent_index: int,
continent_list: list = None, No_of_cells_list: list = None,
nrows_list: list = None, ncol_list: list = None) -> np.ndarray:
Konvertiert eine UNF-Binärdatei in ein mehrdimensionales NumPy-Array.
Die Funktion liest Binärdaten aus einer UNF-Datei sowie Gitter-Koordinaten (GC)
und Gitter-Zeilen (GR) aus separaten Dateien. Die Daten werden gemäß der
Gitterkoordinaten in ein räumliches Array umgewandelt, das für jeden Layer
eine 2D-Gitterstruktur aufweist. Kontinent-Metadaten werden aus config.yaml
geladen (über Paths_and_params), können aber durch optionale Parameter
überschrieben werden.
Parameter
| Parameter | Typ | Beschreibung |
|---|---|---|
file_path |
str |
Pfad zur UNF-Binärdatei GC_path : str Pfad zur Datei mit Gitter-Spalten-Koordinaten GR_path : str Pfad zur Datei mit Gitter-Zeilen-Koordinaten continent_index : int Index des Kontinents in den Metadaten-Listen continent_list : list, optional Liste der Kontinentnamen (wird aus Paths_and_params geladen, falls None) No_of_cells_list : list, optional Liste der Zellanzahl pro Kontinent (wird aus Paths_and_params geladen, falls None) nrows_list : list, optional Liste der Anzahl von Reihen pro Kontinent (wird aus Paths_and_params geladen, falls None) ncol_list : list, optional Liste der Anzahl von Spalten pro Kontinent (wird aus Paths_and_params geladen, falls None) |
Rückgabe
np.ndarray 3D NumPy-Array mit Dimensionen (layer, row, col)
if continent_list is None:
continent_list = PP.name
if No_of_cells_list is None:
No_of_cells_list = PP.ng
if nrows_list is None:
nrows_list = PP.nrow
if ncol_list is None:
ncol_list = PP.ncol
nlayers = getFileInfo(file_path)[0]
cellsInContinent = No_of_cells_list[continent_index]
file_values = ReadBin(file_path, cellsInContinent)
GC = ReadBin(GC_path, cellsInContinent)
GR = ReadBin(GR_path, cellsInContinent)
ndarray = np.zeros((nlayers, nrows_list[continent_index], ncol_list[continent_index]))
Die Gitterkoordinaten werden verwendet, um Werte aus der eindimensionalen Liste in die richtige Position des mehrdimensionalen Arrays zu platzieren. Der Index wird um 1 reduziert, da die GC/GR-Koordinaten bei 1 beginnen, numpy-Indizierung aber bei 0 beginnt.
for i in range(cellsInContinent):
for j in range(nlayers):
Ncol = GC[i]
Nrow = GR[i]
item = file_values[nlayers*(i-1) + j + nlayers]
ndarray[j][Nrow-1][Ncol-1] = item
return ndarray
from osgeo import gdal
from osgeo import osr
def ArrayToRaster(array: np.ndarray, outfile: str, xmin: float, ymax: float,
xres: float, yres: float, ncols: int, nrows: int,
xrot: float = 0, yrot: float = 0, epsg: int = 4326) -> None:
Schreibt ein NumPy-Array als georeferenziertes GeoTIFF-Rasterbild.
Die Funktion nutzt GDAL, um ein 2D-Array in ein GeoTIFF-Format zu exportieren
und dabei vollständige räumliche Referenzierungsinformationen zu bewahren.
Die Geotransformation verknüpft Pixel-Koordinaten mit geografischen Koordinaten.
Parameter
| Parameter | Typ | Beschreibung |
|---|---|---|
array |
np.ndarray |
2D NumPy-Array mit Rasterdaten outfile : str Ausgabepfad für die GeoTIFF-Datei xmin : float Minimale x-Koordinate (westliche Grenze) ymax : float Maximale y-Koordinate (nördliche Grenze) xres : float Auflösung in x-Richtung (Pixelgröße) yres : float Auflösung in y-Richtung (Pixelgröße) ncols : int Anzahl der Spalten nrows : int Anzahl der Zeilen xrot : float, optional Rotationsparameter für x-Richtung (Standard: 0) yrot : float, optional Rotationsparameter für y-Richtung (Standard: 0) epsg : int, optional EPSG-Code für das Koordinatensystem (Standard: 4326 = WGS84) |
Geotransformation definiert die Abbildung zwischen Pixel- und Weltkoordinaten
geotransform=(xmin, xres, xrot, ymax, yrot, -yres)
output_raster = gdal.GetDriverByName('GTiff').Create(outfile, ncols, nrows, 1, gdal.GDT_Float32)
output_raster.SetGeoTransform(geotransform)
Räumliche Referenzierung aus EPSG-Code laden und auf den Raster anwenden
srs = osr.SpatialReference()
srs.ImportFromEPSG(epsg)
output_raster.SetProjection(srs.ExportToWkt())
Array in das erste (und einzige) Rasterband schreiben und auf Speicher leeren
output_raster.GetRasterBand(1).WriteArray(array)
output_raster.FlushCache()
return
def writeBin(filename: str, text: list, nbytes: int = 1) -> None:
Schreibt eine Liste von Ganzzahlen als Big-Endian-Binärdaten in eine Datei.
Parameter
| Parameter | Typ | Beschreibung |
|---|---|---|
filename |
str |
Pfad zur Ausgabedatei text : list Liste von Ganzzahlen, die geschrieben werden sollen nbytes : int, optional Anzahl der Bytes pro Wert (Standard: 1) |
with open(filename, 'wb') as file:
for b in text:
file.write(b.to_bytes(nbytes, byteorder = "big"))
return
def Path_Concatenate(first: str, year: int, last: str) -> str:
Verbindet einen Pfadpräfix, ein Jahr und ein Pfadsuffix zu einem Pfad.
Parameter
| Parameter | Typ | Beschreibung |
|---|---|---|
first |
str |
Anfangsteil des Pfades year : int Jahr als Ganzzahl (wird zu String konvertiert) last : str Endsuffix des Pfades |
Rückgabe
str Vollständiger Pfad als String