Source code for wolfhece.pywalous

"""
Author: HECE - University of Liege, Pierre Archambeau
Date: 2024

Copyright (c) 2024 University of Liege. All rights reserved.

This script and its content are protected by copyright law. Unauthorized
copying or distribution of this file, via any medium, is strictly prohibited.
"""

from osgeo import ogr, gdal
import geopandas as gpd
from os.path import exists, join, dirname, basename
from os import getcwd, chdir
from typing import Union, Literal
import numpy as np
from pathlib import Path
import logging

import wx
import wx.grid

from .PyTranslate import _
from .PyPalette import wolfpalette
from .wolf_array import WolfArray, header_wolf


"""
Hydrology classification for Land Use in Wallonia:
1 = forêt
2 = prairie
3 = culture
4 = pavés/urbain
5 = rivière
6 = plan d'eau
"""

[docs] HYDROLOGY_LANDUSE_FR = {"forêt" : 1., "prairie" : 2., "culture" : 3., "pavés/urbain" : 4., "rivière" : 5., "plan d'eau" : 6., }
[docs] HYDROLOGY_LANDUSE_EN = {"forest" : 1., "meadow" : 2., "crop" : 3., "paved/urban" : 4., "river" : 5., "water body" : 6., }
# WALOUS OCS # ---------- """ Extrait de https://metawal.wallonie.be/geonetwork/srv/api/records/86462606-4a21-49c8-ab9e-564ccba681b7/attachments/DescriptionLegende_WALOUS_OCS.pdf 1. Revêtement artificiel du sol: cette classe reprend tous les revêtements artificiels du sol de nature peu ou pas perméable (ex béton, le bitume ou les pavés). Ceci comprend le réseau routier, les trottoirs, les terrasses, les parkings (les emplacements de parking semiperméables non végétalisés sont également dans cette catégorie) et les terrains ou pistes de sport en matériaux synthétiques. 2. Constructions artificielles hors sol : cette classe reprend tous les bâtiments et autres constructions s'élevant au-dessus du sol. 3. Réseau ferroviaire : cette classe reprend les rails et ballasts des chemins de fer encore en activité (les RaVEL entre dans la classe 1). Cette classe se différencie de la classe 1 par sa grande perméabilité. 4. Sols nus : cette classe inclut tout type de roche mère n'étant couverte par des végétaux supérieurs à aucun moment de l'année. Ces sols sont soit naturels (roches affleurantes, falaises, berges caillouteuses…), soit générés par l'activité humaine (extraction, sols compactés, coupe à blanc de l'année…) 5. Eaux de surface : cette classe comprend toutes les surfaces d'eau libre, naturelles ou artificielles. Ceci inclut donc à la fois les cours d'eau (rivières, fleuves et canaux) et les plans d'eau (mares, étangs, lacs, bassins de décantation, piscines extérieures). 6. Couvert herbacé en rotation : cette classe reprend les parcelles combinant un couvert herbacé une partie de l'année et un sol nu temporairement mis à nu. On y retrouve toutes les cultures annuelles, ainsi que les prairies temporaires succédant à une culture annuelle. 7. Couvert herbacé continu : cette classe reprend tous les sols recouverts par de la végétation herbacée tout au long de l'année. Cette végétation peut être d'origine naturelle (landes, mégaphorbiaies, tourbières, pelouses naturelles, végétation rudérale recolonisant une friche ou une ancienne coupe à blanc…), agricole (prés et prairies non labourés dans la saison) ou artificielle (jardin, terrains de sport, parcs…) 8. Arbres résineux : cette classe comprend tous les arbres de plus de 3 m (isolés, en haie ou en peuplement) du groupe des résineux (gymnosperme). 9. Arbres feuillus : cette classe comprend tous les arbres de plus de 3 m (isolés, en haie ou en peuplement) du groupe des feuillus (angiosperme). 80. Arbustes résineux : cette classe comprend tous les arbres et arbustes de moins de 3 m (isolés, en haie ou en peuplement) du groupe des résineux (gymnosperme). 90. Arbustes feuillus : cette classe comprend tous les arbres et arbuste de moins de 3 m (isolés, en haie ou en peuplement) du groupe des feuillus (angiosperme). <paletteEntry color="#8a8a8a" value="1" alpha="255" label="Revêtement artificiel au sol"/> <paletteEntry color="#dc0f0f" value="2" alpha="255" label="Constructions artificielles hors sol"/> <paletteEntry color="#4e4e4e" value="3" alpha="255" label="Réseau ferroviaire"/> <paletteEntry color="#d0d0d0" value="4" alpha="255" label="Sols nus"/> <paletteEntry color="#2461f7" value="5" alpha="255" label="Eaux de surface"/> <paletteEntry color="#ffff73" value="6" alpha="255" label="Couvert herbacé en rotation dans l'année (ex: culture annuelle)"/> <paletteEntry color="#e9ffbe" value="7" alpha="255" label="Couvert herbacé toute l'année"/> <paletteEntry color="#003200" value="8" alpha="255" label="Résineux (> 3m)"/> <paletteEntry color="#007800" value="80" alpha="255" label="Résineux (≤ 3m)"/> <paletteEntry color="#28c828" value="9" alpha="255" label="Feuillus (> 3m)"/> <paletteEntry color="#b7e8b0" value="90" alpha="255" label="Feuillus (≤ 3m)"/> <paletteEntry color="#e5ea3f" value="0" alpha="0" label="Pas de données"/> """
[docs] WALOUS_OCS = { "Revêtement artificiel au sol": 1., "Constructions artificielles hors sol": 2., "Réseau ferroviaire": 3., "Sols nus": 4., "Eaux de surface": 5., "Couvert herbacé en rotation dans l'année": 6., "Couvert herbacé toute l'année": 7., "Arbres résineux (>= 3m)": 8., "Arbres feuillus (>= 3m)": 9., "Arbustes résineux (< 3m)": 80., "Arbustes feuillus (< 3m)": 90.}
[docs] WALOUS_OCS_COLORMAP = { 0.: (229, 234, 63, 0), # #e5ea3f Pas de données 1.: (138, 138, 138, 255), # #8a8a8a Revêtement artificiel au sol 2.: (220, 15, 15, 255), # #dc0f0f Constructions artificielles hors sol 3.: (78, 78, 78, 255), # #4e4e4e Réseau ferroviaire 4.: (208, 208, 208, 255), # #d0d0d0 Sols nus 5.: (36, 97, 247, 255), # #2461f7 Eaux de surface 6.: (255, 255, 115, 255), # #ffff73 Couvert herbacé en rotation 7.: (233, 255, 190, 255), # #e9ffbe Couvert herbacé continu 8.: (0, 50, 0, 255), # #003200 Arbres résineux (> 3m) 9.: (40, 200, 40, 255), # #28c828 Arbres feuillus (> 3m) 80.: (0, 120, 0, 255), # #007800 Arbustes résineux (≤ 3m) 90.: (183, 232, 176, 255), # #b7e8b0 Arbustes feuillus (≤ 3m) }
[docs] WALOUS_OCS2MANNING = {1.: 0.02, # Revêtement artificiel au sol 2.: 0.025, # Constructions artificielles hors sol 3.: 0.04, # Réseau ferroviaire 4.: 0.04, # Sols nus 5.: 0.033, # Eaux de surface 6.: 0.04, # Couvert herbacé en rotation dans l'année 7.: 0.03, # Couvert herbacé toute l'année 8.: 0.04, # Résineux (> 3m) 9.: 0.04, # Feuillus (> 3m) 80.: 0.03, # Résineux (≤ 3m) 90.: 0.03, # Feuillus (≤ 3m) }
[docs] WALOUS_OCS2HYDROLOGY = {1.: 4., # Revêtement artificiel au sol 2.: 4., # Constructions artificielles hors sol 3.: 4., # Réseau ferroviaire 4.: 4., # Sols nus 5.: 5., # Eaux de surface 6.: 3., # Couvert herbacé en rotation dans l'année 7.: 2., # Couvert herbacé toute l'année 8.: 1., # Résineux (> 3m) 9.: 1., # Feuillus (> 3m) 80.: 3., # Arbustes résineux (< 3m) 90.: 3. # Arbustes feuillus (< 3m) }
# WALOUS UTS # ----------
[docs] WALOUS_UTS_MAJ_NIV1 = {'Production primaire': 1., 'Production secondaire': 2., 'Production tertiaire': 3., 'Réseaux de transport, Logistique et réseaux d\'utilité publique': 4., 'Usage résidentiel': 5., 'Autres usages': 6., 'Zones naturelles': 7.}
[docs] WALOUS_UTS_MAJ_NIV2 = {'Agriculture': 11., 'Sylviculture': 12., 'Industries Extractives': 13., 'Aquaculture et pêche':14, 'Production secondaire non définie': 20., 'Industrie de matières premières': 21., 'Industrie lourde': 22., 'Industrie légère': 23., 'Production d\'énergie': 24., 'Service commerciaux': 31., 'Services financiers, spécialisés et d\'information': 32., 'Services publics': 33., 'Services culturels, Services de loisirs et Services récréatifs': 34., 'Réseaux de transport': 41., 'Services Logistiques et d\'entreposage': 42., 'Réseau d\'utilité publique': 43., 'Usage résidentiel permanent': 51., 'Usage résidentiel avec d\'autres usages compatibles': 52., 'Autres usages résidentiels': 53., 'Zones abandonnées': 62., 'Usage inconnu': 66., 'Zones naturelles': 70.}
[docs] WALOUS_UTS_COLORMAP_MAJ_NIV1 = {1.: (181,230,90,255), 2.: (99,99,99,255), 3.: (159,187,215,255), 4.: (181,121,241,255), 5.: (241,121,99,255), 6.: (221,221,221,255), 7.: (255,255,190,255)}
[docs] WALOUS_UTS_COLORMAP_MAJ_NIV2 = {11.: (153,230,0,255), 12.: (55,168,0,255), 13.: (142,181,180,255), 14.: (172,250,192,255), 20.: (179,179,179,255), 21.: (0,0,0,255), 22.: (157,157,157,255), 23.: (225,225,225,255), 24.: (79,79,79,255), 31.: (0,133,168,255), 32.: (186,236,245,255), 33.: (71,214,242,255), 34.: (102,154,171,255), 41.: (169,0,230,255), 42.: (213,196,245,255), 43.: (180,135,247,255), 51.: (255,0,0,255), 52.: (168,0,0,255), 53.: (255,127,127,255), 62.: (136,69,69,255), 66.: (206,136,102,255), 70.: (255,255,190,255)}
[docs] WALOUS_UTS2MANNING_MAJ_NIV1 = {1.: 0.04, # Production primaire 2.: 0.02, # Production secondaire 3.: 0.02, # Production tertiaire 4.: 0.03, # Réseaux de transport, Logistique et réseaux d'utilité publique 5.: 0.025,# Usage résidentiel 6.: 0.04, # Autres usages 7.: 0.05} # Zones naturelles
[docs] WALOUS_UTS2MANNING_MAJ_NIV2 = {11.: 0.04, # Agriculture 12.: 0.04, # Sylviculture 13.: 0.04, # Industries Extractives 14.: 0.04, # Aquaculture et pêche 20.: 0.03, # Production secondaire non définie 21.: 0.03, # Industrie de matières premières 22.: 0.03, # Industrie lourde 23.: 0.03, # Industrie légère 24.: 0.03, # Production d'énergie 31.: 0.02, # Service commerciaux 32.: 0.02, # Services financiers, spécialisés et d'information 33.: 0.02, # Services publics 34.: 0.02, # Services culturels, Services de loisirs et Services récréatifs 41.: 0.025, # Réseaux de transport 42.: 0.02, # Services Logistiques et d'entreposage 43.: 0.025, # Réseau d'utilité publique 51.: 0.025, # Usage résidentiel permanent 52.: 0.025, # Usage résidentiel avec d'autres usages compatibles 53.: 0.025, # Autres usages résidentiels 62.: 0.04, # Zones abandonnées 66.: 0.04, # Usage inconnu 70.: 0.05} # Zones naturelles
[docs] def get_palette_walous_uts(which:Literal['MAJ_NIV1', 'MAJ_NIV2']) -> wolfpalette: """ Get the palette for WALOUS :return : palette """ locpal = wolfpalette() locpal.interval_cst = True locpal.automatic = False if which == 'MAJ_NIV1': locpal.set_values_colors(values = list(WALOUS_UTS_MAJ_NIV1.values()), colors = list(WALOUS_UTS_COLORMAP_MAJ_NIV1.values())) elif which == 'MAJ_NIV2': locpal.set_values_colors(values = list(WALOUS_UTS_MAJ_NIV2.values()), colors = list(WALOUS_UTS_COLORMAP_MAJ_NIV2.values())) else: logging.error('Unknown WALOUS level') return locpal
[docs] def get_palette_walous_ocs() -> wolfpalette: """ Get the palette for WALOUS OCS :return : palette """ locpal = wolfpalette() locpal.interval_cst = True locpal.automatic = False locpal.set_values_colors(values = list(WALOUS_OCS_COLORMAP.keys()), colors = list(WALOUS_OCS_COLORMAP.values())) return locpal
[docs] def update_palette_walous_uts(which:Literal['UTS_MAJ_NIV1', 'UTS_MAJ_NIV2'], pal:wolfpalette): """ Update the palette for WALOUS MAJ_NIV1 :param pal : palette to update :return : updated palette """ if which in ['UTS_MAJ_NIV1', 'MAJ_NIV1']: for k, v in WALOUS_UTS_COLORMAP_MAJ_NIV1.items(): pal.set_values_colors(values = [val - 1e-6 for val in list(WALOUS_UTS_MAJ_NIV1.values())], colors = list(WALOUS_UTS_COLORMAP_MAJ_NIV1.values())) elif which in ['UTS_MAJ_NIV2', 'MAJ_NIV2']: for k, v in WALOUS_UTS_COLORMAP_MAJ_NIV2.items(): pal.set_values_colors(values = [val - 1e-6 for val in list(WALOUS_UTS_MAJ_NIV2.values())], colors = list(WALOUS_UTS_COLORMAP_MAJ_NIV2.values())) pal.interval_cst = True pal.automatic = False return 0
[docs] def update_palette_walous_ocs(pal:wolfpalette): """ Update the palette for WALOUS OCS :param pal : palette to update :return : updated palette """ for k, v in WALOUS_OCS_COLORMAP.items(): pal.set_values_colors(values = [val - 1e-6 for val in list(WALOUS_OCS_COLORMAP.keys())], colors = list(WALOUS_OCS_COLORMAP.values())) pal.interval_cst = True pal.automatic = False return 0
[docs] class Walous_data(): """ La donnée Walous est liée à l'utilisation des sols en Wallonie source : https://geoportail.wallonie.be/walous Cette classe permet la manipulation de la donnée dans le cadre du projet MODREC et plus spécifiquement la distribution d'un coefficient de frottement sur base de la donnée Walous. """ def __init__(self, dir_data:str = '', fn:str = 'WAL_UTS__2018_L72', extension:str = '.shp', bounds:Union[list[float, float, float, float],list[list[float, float], list[float, float]]] = None) -> None: """ Constructor :param dir_data : directory of the data :param fn : filename without extension :param extension : extension of the file (.shp, .gpkg, ...) :param bounds : Two ways to set spatial bounds -- [xmin, ymin, xmax, ymax] or [[xmin, xmax], [ymin, ymax]] """
[docs] self._dir = str(dir_data) # directory of the data
[docs] self._fn = str(fn) # filename without extension
[docs] self._extension = extension # extension of the file
[docs] self._gdf = None # geopandas dataframe
if bounds is not None: # Bounds are set -> read file self.read(True, bounds=bounds)
[docs] def read(self, force:bool = False, bounds:Union[list[float, float, float, float],list[list[float, float], list[float, float]]] = None): """ Read data from file :param force : force to read even read was done before :param bounds : [xmin, ymin, xmax, ymax] or [[xmin, xmax], [ymin, ymax]] """ if self._gdf is None or force: assert self._dir!="" and self._fn != '' filepath = (Path(self._dir)/ self._fn).with_suffix(self._extension) if filepath.exists(): if bounds is not None: if len(bounds)==2: # [[xmin, xmax], [ymin, ymax]] xmin = bounds[0][0] xmax = bounds[0][1] ymin = bounds[1][0] ymax = bounds[1][1] else: # [xmin, ymin, xmax, ymax] xmin = bounds[0] xmax = bounds[2] ymin = bounds[1] ymax = bounds[3] # read part of the file self._gdf = gpd.read_file(str(filepath), bbox=(xmin,ymin,xmax,ymax)) else: # read all self._gdf = gpd.read_file(str(filepath)) # self._gdf['MAJ_NIV2'] = np.asarray(self._gdf['MAJ_NIV2'], dtype=int) # self._gdf['MAJ_NIV3'] = np.asarray(self._gdf['MAJ_NIV3'], dtype=int) # ne fonctionne pas car le niveau 3 contient des lettres --> à généraliser else: self._gdf = None
[docs] def write(self, fnout:str='out_clip.shp'): """ Write _gdf to file :param fnout : output filename """ try: curdir = getcwd() detsdir = dirname(fnout) chdir(detsdir) fnout = str((Path(fnout)).with_suffix(self._extension)) self._gdf.to_file(basename(fnout)) chdir(curdir) return 0 except: logging.error('Error in writing data - Walous module') return -1
[docs] def to_file(self, fn:str='out_clip.shp'): """ Alias to write :param fn : output filename """ self.write(fn)
[docs] def rasterize(self, bounds:Union[list[float, float, float, float],list[list[float, float], list[float, float]]], layer:Literal['UTS_MAJ_NIV1','UTS_MAJ_NIV2'] = 'UTS_MAJ_NIV1', fn_out:str = 'out.tif', pixel_size:float = 0.5, NoData_value:float = -99999., num_type = gdal.GDT_Float32, ): """ Rasterization of polygon data to tif. :param bounds : [xmin, ymin, xmax, ymax] or [[xmin, xmax], [ymin, ymax]] :param layer : layer to rasterize :param fn_out : output filename :param pixel_size : pixel size :param NoData_value : NoData value :param num_type : type of the number """ layer = layer.upper().replace('UTS_', '') if bounds is None: logging.error('Bounds must be set') return None else: if len(bounds)==4: # [[xmin, xmax], [ymin, ymax]] bounds = [[bounds[0], bounds[2]], [bounds[1], bounds[3]]] if self._gdf is None: logging.info('Reading data') self.read(bounds=bounds) logging.info('End of reading data') if self._gdf is None: logging.error('No data to rasterize') return None if layer not in self._gdf.keys(): logging.error('Layer not found in the data') return None ret = self.write(str(fn_out) + '.shp') if ret !=0: logging.error('An error occured in writing the data to file') return -1 try: # Add a new column for mapping based on the desired layer if layer == 'MAJ_NIV2': self._gdf['Mapping'] = np.float32(self._gdf[layer].replace('_', '')) else: self._gdf['Mapping'] = np.float32(self._gdf[layer]) source_ds:ogr.DataSource source_layer:ogr.Layer source_ds = ogr.Open(self._gdf.to_json()) # source_srs:ogr. source_layer = source_ds.GetLayer() source_srs = source_layer.GetSpatialRef() # Create the destination data source x_res = int((bounds[0][1] - bounds[0][0]) / pixel_size) y_res = int((bounds[1][1] - bounds[1][0]) / pixel_size) driver:gdal.Driver driver = gdal.GetDriverByName('GTiff') target_ds = driver.Create(str(fn_out), x_res, y_res, 1, num_type) target_ds:gdal.Dataset band:gdal.Band target_ds.SetGeoTransform((bounds[0][0], pixel_size, 0, bounds[1][1], 0, -pixel_size)) band = target_ds.GetRasterBand(1) band.SetNoDataValue(NoData_value) target_ds.SetProjection(source_srs.ExportToWkt()) # Rasterize gdal.RasterizeLayer(target_ds, [1], source_layer, options = ["ALL_TOUCHED=TRUE", "ATTRIBUTE=Mapping"]) target_ds = None return fn_out except: logging.error('Error in rasterization') return -2
[docs] class DlgMapWalous2Manning(wx.Dialog): """ Modal dialog for mapping WALOUS value to another ones """ def __init__(self, parent, title:str = _("Mapping WALOUS value to ..."), which:Literal['UTS_MAJ_NIV1', 'UTS_MAJ_NIV2', 'OCS_MANNING', 'OCS_HYDROLOGY'] = 'UTS_MAJ_NIV1'): super(DlgMapWalous2Manning, self).__init__(parent, title=title, size=(450, 400)) panel = wx.Panel(self) sizer = wx.BoxSizer(wx.VERTICAL)
[docs] self._table = wx.grid.Grid(panel)
if which == 'UTS_MAJ_NIV1': self._table.CreateGrid(len(WALOUS_UTS_MAJ_NIV1), 3) self._table.SetColLabelValue(0, _("Name")) self._table.SetColLabelValue(1, _("Value - UTS")) self._table.SetColLabelValue(2, _("Manning 'n'")) self._table.HideRowLabels() for i, (k, v) in enumerate(WALOUS_UTS_MAJ_NIV1.items()): self._table.SetCellValue(i, 0, str(k)) self._table.SetCellValue(i, 1, str(v)) self._table.SetCellValue(i, 2, str(WALOUS_UTS2MANNING_MAJ_NIV1[v])) self._table.SetCellAlignment(i, 1, wx.ALIGN_CENTER, wx.ALIGN_CENTER) self._table.SetCellAlignment(i, 2, wx.ALIGN_CENTER, wx.ALIGN_CENTER) elif which == 'UTS_MAJ_NIV2': self._table.CreateGrid(len(WALOUS_UTS_MAJ_NIV2), 3) self._table.SetColLabelValue(0, _("Name")) self._table.SetColLabelValue(1, _("Value - UTS")) self._table.SetColLabelValue(2, _("Manning 'n'")) self._table.HideRowLabels() for i, (k, v) in enumerate(WALOUS_UTS_MAJ_NIV2.items()): self._table.SetCellValue(i, 0, str(k)) self._table.SetCellValue(i, 1, str(v)) self._table.SetCellValue(i, 2, str(WALOUS_UTS2MANNING_MAJ_NIV2[v])) self._table.SetCellAlignment(i, 1, wx.ALIGN_CENTER, wx.ALIGN_CENTER) self._table.SetCellAlignment(i, 2, wx.ALIGN_CENTER, wx.ALIGN_CENTER) elif which == 'OCS_MANNING': self._table.CreateGrid(len(WALOUS_OCS2MANNING), 3) self._table.SetColLabelValue(0, _("Name")) self._table.SetColLabelValue(1, _("Value - OCS")) self._table.SetColLabelValue(2, _("Manning 'n'")) self._table.HideRowLabels() for i, (k, v) in enumerate(WALOUS_OCS.items()): self._table.SetCellValue(i, 0, str(k)) self._table.SetCellValue(i, 1, str(v)) self._table.SetCellValue(i, 2, str(WALOUS_OCS2MANNING[v])) self._table.SetCellAlignment(i, 1, wx.ALIGN_CENTER, wx.ALIGN_CENTER) self._table.SetCellAlignment(i, 2, wx.ALIGN_CENTER, wx.ALIGN_CENTER) elif which == 'OCS_HYDROLOGY': self._table.CreateGrid(len(WALOUS_OCS2HYDROLOGY), 3) self._table.SetColLabelValue(0, _("Name")) self._table.SetColLabelValue(1, _("Value - OCS")) self._table.SetColLabelValue(2, _("LandUse - Hydrology")) self._table.HideRowLabels() for i, (k, v) in enumerate(WALOUS_OCS2HYDROLOGY.items()): self._table.SetCellValue(i, 0, str(k)) self._table.SetCellValue(i, 1, str(v)) self._table.SetCellValue(i, 2, str(WALOUS_OCS2HYDROLOGY[v])) self._table.SetCellAlignment(i, 1, wx.ALIGN_CENTER, wx.ALIGN_CENTER) self._table.SetCellAlignment(i, 2, wx.ALIGN_CENTER, wx.ALIGN_CENTER) self._table.SetColFormatFloat(1, 2, 0) self._table.SetColFormatFloat(2, 6, 4) self._table.SetColSize(0, 250) self._table.SetColSize(1, 50) self._table.SetColSize(2, 100) sizer.Add(self._table, 1, wx.EXPAND) panel.SetSizer(sizer) sizer_btns = wx.BoxSizer(wx.HORIZONTAL) btn_ok = wx.Button(panel, wx.ID_OK, _("OK")) btn_cancel = wx.Button(panel, wx.ID_CANCEL, _("Cancel")) sizer_btns.Add(btn_ok, 0, wx.ALL, 5) sizer_btns.Add(btn_cancel, 0, wx.ALL, 5) sizer.Add(sizer_btns, 0, wx.ALIGN_CENTER) self.Bind(wx.EVT_BUTTON, self.on_ok, btn_ok) self.Bind(wx.EVT_BUTTON, self.on_cancel, btn_cancel) self.Center()
[docs] def on_ok(self, event): self.EndModal(wx.ID_OK)
[docs] def on_cancel(self, event): self.EndModal(wx.ID_CANCEL)
[docs] def get_mapping(self) -> dict: retdict = {} try: for i in range(self._table.GetNumberRows()): retdict[float(self._table.GetCellValue(i, 1))] = float(self._table.GetCellValue(i, 2)) except: retdict = -1 logging.error('Error in getting mapping') return retdict
[docs] class DlgMapWalousOCS2Manning(DlgMapWalous2Manning): """ Modal dialog for mapping WALOUS value to another ones This dialog is used to map WALOUS values to hydrology values. It inherits from DlgMapWalous2Manning and overrides the initialization to set the correct column labels and values. """ def __init__(self, parent, title:str = _("Mapping WALOUS value to ...")): super(DlgMapWalousOCS2Manning, self).__init__(parent, title= title, which='OCS_MANNING') self._table.SetColLabelValue(2, _("Land Use - OCS")) for i, (k, v) in enumerate(WALOUS_OCS2MANNING.items()): self._table.SetColLabelValue(1, _("Value - OCS")) self._table.SetCellValue(i, 2, str(v))
[docs] class DlgMapWalous2Hydrology(DlgMapWalous2Manning): """ Modal dialog for mapping WALOUS value to another ones This dialog is used to map WALOUS values to hydrology values. It inherits from DlgMapWalous2Manning and overrides the initialization to set the correct column labels and values. """ def __init__(self, parent, title:str = _("Mapping WALOUS value to ...")): super(DlgMapWalous2Hydrology, self).__init__(parent, title= title, which='OCS_HYDROLOGY') self._table.SetColLabelValue(2, _("Land Use - OCS")) for i, (k, v) in enumerate(WALOUS_OCS2HYDROLOGY.items()): self._table.SetColLabelValue(1, _("Value - OCS")) self._table.SetCellValue(i, 2, str(v))
[docs] class Walous_UTS_Legend(wx.Dialog): """ Show the legend of WALOUS """ def __init__(self, *args, **kw): super().__init__(*args, **kw, size = (400, 650)) panel = wx.Panel(self) sizer = wx.BoxSizer(wx.VERTICAL)
[docs] self._text_v1 = wx.StaticText(panel, label=_("WALOUS MAJ_NIV1"))
[docs] self._text_v2 = wx.StaticText(panel, label=_("WALOUS MAJ_NIV2"))
[docs] self._legend_v1 = wx.grid.Grid(panel)
self._legend_v1.CreateGrid(len(WALOUS_UTS_MAJ_NIV1), 3) self._legend_v1.SetColSize(0, 200) self._legend_v1.SetColLabelValue(0, _("Name")) self._legend_v1.SetColLabelValue(1, _("Value")) self._legend_v1.SetColLabelValue(2, _("Color")) self._legend_v1.HideRowLabels() for i, (k, v) in enumerate(WALOUS_UTS_MAJ_NIV1.items()): self._legend_v1.SetCellValue(i, 0, str(k)) self._legend_v1.SetCellValue(i, 1, str(v)) self._legend_v1.SetCellBackgroundColour(i, 2, WALOUS_UTS_COLORMAP_MAJ_NIV1[v])
[docs] self._legend_v2 = wx.grid.Grid(panel)
self._legend_v2.CreateGrid(len(WALOUS_UTS_MAJ_NIV2), 3) self._legend_v2.SetColLabelValue(0, _("Name")) self._legend_v2.SetColSize(0, 200) self._legend_v2.SetColLabelValue(1, _("Value")) self._legend_v2.SetColLabelValue(2, _("Color")) self._legend_v2.HideRowLabels() for i, (k, v) in enumerate(WALOUS_UTS_MAJ_NIV2.items()): self._legend_v2.SetCellValue(i, 0, str(k)) self._legend_v2.SetCellValue(i, 1, str(v)) self._legend_v2.SetCellBackgroundColour(i, 2, WALOUS_UTS_COLORMAP_MAJ_NIV2[v]) sizer.Add(self._text_v1, 0, wx.EXPAND, border=5) sizer.Add(self._legend_v1, 1, wx.EXPAND, border=5) sizer.Add(self._text_v2, 0, wx.EXPAND, border=5) sizer.Add(self._legend_v2, 1, wx.EXPAND, border=5) panel.SetSizer(sizer) self.Center() self.Show() def __del__(self): self.Destroy()
[docs] def close(self): self.Destroy()
[docs] class Walous_OCS_Legend(wx.Dialog): """ Show the legend of WALOUS OCS """ def __init__(self, *args, **kw): super().__init__(*args, **kw, size = (400, 650)) panel = wx.Panel(self) sizer = wx.BoxSizer(wx.VERTICAL)
[docs] self._text = wx.StaticText(panel, label=_("WALOUS OCS"))
[docs] self._legend = wx.grid.Grid(panel)
self._legend.CreateGrid(len(WALOUS_OCS_COLORMAP), 3) self._legend.SetColSize(0, 200) self._legend.SetColLabelValue(0, _("Name")) self._legend.SetColLabelValue(1, _("Value")) self._legend.SetColLabelValue(2, _("Color")) self._legend.HideRowLabels() for i, (k, v) in enumerate(WALOUS_OCS_COLORMAP.items()): self._legend.SetCellValue(i, 0, str(k)) self._legend.SetCellValue(i, 1, str(v)) self._legend.SetCellBackgroundColour(i, 2, v) sizer.Add(self._text, 0, wx.EXPAND, border=5) sizer.Add(self._legend, 1, wx.EXPAND, border=5) panel.SetSizer(sizer) self.Center() self.Show() def __del__(self): self.Destroy()
[docs] def close(self): self.Destroy()
[docs] def get_array_WALOUS_OCS(fname:Path, spatial_res:float, bounds:Union[list[float, float, float, float],list[list[float, float], list[float, float]]]) -> Union[WolfArray, None]: """ Get WALOUS OCS as WolfArray :param fname: path to the WALOUS OCS file :param spatial_res: desired spatial resolution (in m) :param bounds: [xmin, ymin, xmax, ymax] or [[xmin, xmax], [ymin, ymax]] :return: WolfArray or None if error """ if not fname.exists(): logging.error(_('WALOUS OCS file not found: {}').format(fname)) return None # Convert bounds to [[xmin, xmax], [ymin, ymax]] if needed if len(bounds)==4: bounds = [[bounds[0], bounds[2]], [bounds[1], bounds[3]]] # Convert bvounds to list to be able to modify it bounds = [list(bounds[0]), list(bounds[1])] bounds_copy = [list(bounds[0]), list(bounds[1])] header_OCS = header_wolf.read_header(fname) if header_OCS.dx != spatial_res: # Adapt bounds to ensure that the rebin will be correct. # If spatial_res is a multiple of header_OCS.dx, no need to change bounds. # If not, change the bounds to ensure that the crop will include data in all footprints. if (bounds[0][0] - header_OCS.origx) % header_OCS.dx != 0: bounds[0][0] = header_OCS.origx + ((bounds[0][0] - header_OCS.origx) // header_OCS.dx) * header_OCS.dx if (bounds[0][1] - bounds[0][0]) % header_OCS.dx != 0: bounds[0][1] = bounds[0][0] + ((bounds[0][1] - bounds[0][0]) // header_OCS.dx + 1) * header_OCS.dx if (bounds[1][0] - header_OCS.origy) % header_OCS.dy != 0: bounds[1][0] = header_OCS.origy + ((bounds[1][0] - header_OCS.origy) // header_OCS.dy) * header_OCS.dy if (bounds[1][1] - bounds[1][0]) % header_OCS.dy != 0: bounds[1][1] = bounds[1][0] + ((bounds[1][1] - bounds[1][0]) // header_OCS.dy + 1) * header_OCS.dy locwalous = WolfArray(fname=fname, crop = [bounds[0][0], bounds[0][1], bounds[1][0], bounds[1][1]]) if locwalous.dx != spatial_res: locwalous.rebin(spatial_res / locwalous.dx, operation='min') logging.info(_('Rebin to {} m because original data are {} m').format(spatial_res, locwalous.dx)) locwalous = WolfArray(mold=locwalous, crop = bounds_copy) return locwalous
[docs] def get_array_WALOUS_OCS_2023_10m(spatial_res:float, bounds:Union[list[float, float, float, float],list[list[float, float], list[float, float]]]) -> Union[WolfArray, None]: """ Get WALOUS OCS 2023 as WolfArray :param spatial_res: desired spatial resolution (in m) :param bounds: [xmin, ymin, xmax, ymax] or [[xmin, xmax], [ymin, ymax]] :return: WolfArray or None if error """ from wolfhece.pydownloader import toys_dataset fname = toys_dataset(dir='Walous_OCS', file='WALOUS_2023_lbt72_10m.tif') return get_array_WALOUS_OCS(fname, spatial_res, bounds)
[docs] def get_array_WALOUS_OCS_2023_4m(spatial_res:float, bounds:Union[list[float, float, float, float],list[list[float, float], list[float, float]]]) -> Union[WolfArray, None]: """ Get WALOUS OCS 2023 as WolfArray :param spatial_res: desired spatial resolution (in m) :param bounds: [xmin, ymin, xmax, ymax] or [[xmin, xmax], [ymin, ymax]] :return: WolfArray or None if error """ from wolfhece.pydownloader import toys_dataset fname = toys_dataset(dir='Walous_OCS', file='WALOUS_2023_lbt72_4m.tif') return get_array_WALOUS_OCS(fname, spatial_res, bounds)
[docs] def get_array_WALOUS_OCS_2020_10m(spatial_res:float, bounds:Union[list[float, float, float, float],list[list[float, float], list[float, float]]]) -> Union[WolfArray, None]: """ Get WALOUS OCS 2020 as WolfArray :param spatial_res: desired spatial resolution (in m) :param bounds: [xmin, ymin, xmax, ymax] or [[xmin, xmax], [ymin, ymax]] :return: WolfArray or None if error """ from wolfhece.pydownloader import toys_dataset fname = toys_dataset(dir='Walous_OCS', file='WALOUS_2020_lbt72_10m.tif') return get_array_WALOUS_OCS(fname, spatial_res, bounds)
[docs] def get_array_WALOUS_OCS_2020_4m(spatial_res:float, bounds:Union[list[float, float, float, float],list[list[float, float], list[float, float]]]) -> Union[WolfArray, None]: """ Get WALOUS OCS 2020 as WolfArray :param spatial_res: desired spatial resolution (in m) :param bounds: [xmin, ymin, xmax, ymax] or [[xmin, xmax], [ymin, ymax]] :return: WolfArray or None if error """ from wolfhece.pydownloader import toys_dataset fname = toys_dataset(dir='Walous_OCS', file='WALOUS_2020_lbt72_4m.tif') return get_array_WALOUS_OCS(fname, spatial_res, bounds)
[docs] def get_array_WALOUS_UTS(fname:Path, fnout:Path, spatial_res:float, bounds:Union[list[float, float, float, float],list[list[float, float], list[float, float]]], which:Literal['UTS_MAJ_NIV1', 'UTS_MAJ_NIV2'] = 'UTS_MAJ_NIV1') -> Union[WolfArray, None]: """ Get WALOUS UTS as WolfArray :param fname: path to the WALOUS UTS file :param spatial_res: desired spatial resolution (in m) :param bounds: [xmin, ymin, xmax, ymax] or [[xmin, xmax], [ymin, ymax]] :param which: which level to use ('UTS_MAJ_NIV1' or 'UTS_MAJ_NIV2') :return: WolfArray or None if error """ fname = Path(fname) fnout = Path(fnout) if not fname.exists(): logging.error(_('WALOUS UTS file not found: {}').format(fname)) return None # Convert bounds to [[xmin, xmax], [ymin, ymax]] if needed if len(bounds)==4: bounds = [[bounds[0], bounds[2]], [bounds[1], bounds[3]]] # Convert bvounds to list to be able to modify it bounds = [list(bounds[0]), list(bounds[1])] bounds_copy = [list(bounds[0]), list(bounds[1])] locwalous = Walous_data(fname.parent, fname.name, extension=fname.suffix, bounds=bounds) ret = locwalous.rasterize(bounds=bounds, layer= which, fn_out=fnout.with_suffix('.tif'), pixel_size=spatial_res) ret = Path(ret) if ret.exists(): locwalous_array = WolfArray(fname=ret) return locwalous_array else: return None