wolfhece 2.1.46__py3-none-any.whl → 2.1.48__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,189 @@
1
+ from pyproj import Transformer
2
+ from concurrent.futures import ProcessPoolExecutor
3
+ import numpy as np
4
+ from osgeo import gdal
5
+ import os
6
+ import logging
7
+
8
+ from .PyTranslate import _
9
+
10
+ def transform_chunk_coordinates(inputEPSG:str, outputEPSG:str, chunk:np.ndarray):
11
+ """
12
+ Transforms a chunk of coordinates
13
+
14
+ :param inputEPSG: input EPSG code (e.g. "EPSG:3812")
15
+ :type inputEPSG: str
16
+ :param outputEPSG: output EPSG code (e.g. "EPSG:31370")
17
+ :type outputEPSG: str
18
+ :param chunk: list of points to be transformed
19
+ :type chunk: np.ndarray
20
+ """
21
+ COO_TRANSFORMER = Transformer.from_crs(inputEPSG, outputEPSG, always_xy=True)
22
+ return COO_TRANSFORMER.transform(chunk[:, 0], chunk[:, 1])
23
+
24
+ def transform_coordinates(points:np.ndarray, inputEPSG:str="EPSG:3812", outputEPSG:str="EPSG:31370", chunk_size:int=1000):
25
+ """
26
+ Transforms coordinates in batches using multiprocessing. If more than chunk_size points are provided, the
27
+ function will split the points into chunks and transform them in parallel => requiring in the main script
28
+ to use the statement if __name__ == '__main__':.
29
+
30
+ :param points: Array of coordinates to be transformed
31
+ :type points: numpy.ndarray
32
+ :param inputEPSG: (optional) Input EPSG code. Defaults to "EPSG:3812"
33
+ :type inputEPSG: str
34
+ :param outputEPSG: (optional) Output EPSG code. Defaults to "EPSG:31370"
35
+ :type outputEPSG: str
36
+ :param chunk_size: (optional) Size of each batch for transformation. Defaults to 100000
37
+ :type chunk_size: int
38
+
39
+ :return numpy.ndarray: Transformed coordinates
40
+ """
41
+
42
+ # sanitize inputs
43
+ inputEPSG = str(inputEPSG)
44
+ outputEPSG = str(outputEPSG)
45
+
46
+ if not "EPSG" in inputEPSG or not "EPSG" in inputEPSG:
47
+ logging.error(_("EPSG code must be in the format 'EPSG:XXXX'"))
48
+ return
49
+
50
+ num_points = len(points)
51
+ results = []
52
+
53
+ total_steps = (num_points + chunk_size - 1) // chunk_size
54
+
55
+ if total_steps == 1:
56
+ result_x, result_y = transform_chunk_coordinates(inputEPSG, outputEPSG, points)
57
+ return np.vstack((result_x, result_y)).T
58
+
59
+ with ProcessPoolExecutor() as executor:
60
+ futures = []
61
+ for i in range(0, num_points, chunk_size):
62
+ chunk = points[i:i + chunk_size]
63
+ futures.append(executor.submit(transform_chunk_coordinates, inputEPSG, outputEPSG, chunk))
64
+
65
+ for step, future in enumerate(futures):
66
+ result_x, result_y = future.result()
67
+ results.append(np.vstack((result_x, result_y)).T)
68
+
69
+ return np.vstack(results)
70
+
71
+ def reproject_and_resample_raster(input_raster_path:str, output_raster_path:str,
72
+ input_srs:str='EPSG:3812', output_srs:str='EPSG:31370',
73
+ resampling_method:str|int=gdal.GRA_Bilinear,
74
+ xRes:float=0.5, yRes:float=0.5, debug:bool=False):
75
+ """
76
+ Use gdal to open a tiff raster in a given input EPSG 'inputEPSG' and transforms the raster into another EPSG system 'outputEPSG'.
77
+ The resolution can be forced through xRes and yRes (the origin will be rounded to the nearest multiple of the resolution). The
78
+ resampling method can be chosen among the gdal GRA_* constants (gdal.GRA_Average; gdal.GRA_Bilinear; gdal.GRA_Cubic; gdal.GRA_CubicSpline;
79
+ gdal.GRA_Lanczos; gdal.GRA_Mode; gdal.GRA_NearestNeighbor).
80
+
81
+ :param input_raster_path: the path to the input raster file (.tif or .tiff)
82
+ :type input_raster_path: str
83
+ :param output_raster_path: the path to the output raster file (.tif or .tiff) that will be created
84
+ :type output_raster_path: str
85
+ :param input_srs: Input EPSG code. Defaults to Lambert 2008 "EPSG:3812"
86
+ :type input_srs: str
87
+ :param output_srs: Output EPSG code. Defaults to Lambert 72 "EPSG:31370"
88
+ :type output_srs: str
89
+ :param resampling_method: Resampling method. Defaults to gdal.GRA_Bilinear
90
+ :type resampling_method: int, str
91
+ :param xRes: Resolution along X. Defaults to 0.5
92
+ :type xRes: float
93
+ :param yRes: Resolution along Y. Defaults to 0.5
94
+ :type yRes: float
95
+ :param debug: If True, print debug information. Defaults to False
96
+ :type debug: bool
97
+ """
98
+ from osgeo import osr
99
+
100
+ # santitize inputs
101
+ input_raster_path = str(input_raster_path)
102
+ output_raster_path = str(output_raster_path)
103
+
104
+ # ATTENTION: mask values should be negative in the tiff corresponding to input_raster_path!
105
+ if not(input_raster_path.endswith('.tif') or input_raster_path.endswith('.tiff')):
106
+ logging.error(_("Input raster must be a GeoTIFF file"))
107
+ return
108
+ if not(output_raster_path.endswith('.tif') or output_raster_path.endswith('.tiff')):
109
+ logging.error(_("Output raster must be a GeoTIFF file"))
110
+ return
111
+
112
+ # check the output file
113
+ if os.path.exists(output_raster_path):
114
+ try:
115
+ os.remove(output_raster_path)
116
+ except PermissionError as e:
117
+ logging.error(_(f"Permission denied while trying to delete {output_raster_path}. Ensure the file is not open in another program and you have sufficient privileges."))
118
+ return
119
+ except Exception as e:
120
+ logging.error(_(f"An unexpected error occurred while trying to delete {output_raster_path}: {str(e)}"))
121
+ return
122
+
123
+ # Open the input raster
124
+ input_raster = gdal.Open(input_raster_path)
125
+ if input_raster is None:
126
+ logging.error(_(f"Unable to open input raster: {input_raster_path}"))
127
+ return
128
+
129
+ # Get the source SRS from the input raster
130
+ source_srs = osr.SpatialReference()
131
+ if debug:
132
+ print('Initial projection: ',input_raster.GetProjection())
133
+ print('set projection init: ',int(input_srs.split(':')[1]))
134
+ source_srs.ImportFromEPSG(int(input_srs.split(':')[1]))
135
+
136
+ # Create the target SRS
137
+ target_srs = osr.SpatialReference()
138
+ if debug:
139
+ print('set projection out: ',int(output_srs.split(':')[1]))
140
+ target_srs.ImportFromEPSG(int(output_srs.split(':')[1]))
141
+
142
+ # Define the options for the reprojection
143
+ # Load the initial array to obtain the origin and the limits
144
+ ulx, xres, xskew, uly, yskew, yres = input_raster.GetGeoTransform()
145
+ Orig = np.array([[ulx,uly],
146
+ [ulx+input_raster.RasterXSize*xres, uly+input_raster.RasterYSize*yres]])
147
+ Orig.sort(0)
148
+
149
+ # Transform the origin and the limits into the new projection 'Lambert 72'
150
+ Orig_out = transform_coordinates(Orig, inputEPSG=input_srs, outputEPSG=output_srs)
151
+
152
+ # Round each coordinate to the nearest multiple of the wanted resolution
153
+ Orig_out[:,0] = np.round(Orig_out[:,0]/xRes)*xRes
154
+ Orig_out[:,1] = np.round(Orig_out[:,1]/yRes)*yRes
155
+ if debug:
156
+ print(Orig_out)
157
+ print(tuple(Orig_out.reshape(-1)))
158
+
159
+ # Define the reprojection options
160
+ # outputBounds=tuple(Orig_out.reshape(-1)),
161
+ # xRes=xRes, yRes=yRes,
162
+ reproject_options = gdal.WarpOptions(
163
+ outputBounds=tuple(Orig_out.reshape(-1)), # Output bounds: (minX, minY, maxX, maxY)
164
+ xRes=xRes, yRes=yRes,
165
+ srcSRS=source_srs.ExportToWkt(),
166
+ dstSRS=target_srs.ExportToWkt(),
167
+ resampleAlg=resampling_method
168
+ )
169
+
170
+ # Reproject and resample the input raster
171
+ output_raster = gdal.Warp(
172
+ destNameOrDestDS=output_raster_path,
173
+ srcDSOrSrcDSTab=input_raster,
174
+ options=reproject_options
175
+ )
176
+
177
+ if output_raster is None:
178
+ logging.error(_(f"Reprojection failed for input raster: {input_raster_path}"))
179
+ return
180
+
181
+ # Flush cache to ensure the output is written to disk
182
+ output_raster.FlushCache()
183
+
184
+ # Close the datasets IMPORTANT to set to None in order to close them
185
+ input_raster = None
186
+ output_raster = None
187
+
188
+ if debug:
189
+ print(f"Reprojection and resampling completed successfully. Output saved to: {output_raster_path}")
wolfhece/apps/version.py CHANGED
@@ -5,7 +5,7 @@ class WolfVersion():
5
5
 
6
6
  self.major = 2
7
7
  self.minor = 1
8
- self.patch = 46
8
+ self.patch = 48
9
9
 
10
10
  def __str__(self):
11
11
 
wolfhece/wolf_array.py CHANGED
@@ -20,6 +20,7 @@ import numpy.ma as ma
20
20
  import math as m
21
21
  import logging
22
22
  import json
23
+ import tempfile
23
24
  from pathlib import Path
24
25
 
25
26
  try:
@@ -45,7 +46,11 @@ from os.path import dirname,basename,join
45
46
  import logging
46
47
  from typing import Literal
47
48
  from copy import deepcopy
49
+ from osgeo import gdal
50
+ from enum import Enum
48
51
 
52
+
53
+ from .Coordinates_operations import reproject_and_resample_raster
49
54
  from .PyTranslate import _
50
55
  from .GraphNotebook import PlotPanel
51
56
  from .CpGrid import CpGrid
@@ -90,6 +95,42 @@ WOLF_ARRAY_MB = [WOLF_ARRAY_MB_SINGLE, WOLF_ARRAY_MB_INTEGER, WOLF_ARRAY_MNAP_IN
90
95
 
91
96
  VERSION_RGB = 2
92
97
 
98
+ class Rebin_Ops(Enum):
99
+ MIN = 0
100
+ MEAN = 1
101
+ MAX = 2
102
+ SUM = 3
103
+ MEDIAN = 4
104
+
105
+ @classmethod
106
+ def get_numpy_ops(cls):
107
+ """ Return a list of numpy functions corresponding to the enum values """
108
+
109
+ # CAUTION : Order is important and must match the enum values
110
+ return [np.ma.min, np.ma.mean, np.ma.max, np.ma.sum, np.ma.median]
111
+
112
+ @classmethod
113
+ def get_ops(cls, name:str):
114
+ """ Return the numpy function corresponding to a string """
115
+
116
+ if isinstance(name, Rebin_Ops):
117
+ return cls.get_numpy_ops()[name.value]
118
+ elif isinstance(name, str):
119
+ if name == 'min':
120
+ return np.ma.min
121
+ elif name == 'mean':
122
+ return np.ma.mean
123
+ elif name == 'max':
124
+ return np.ma.max
125
+ elif name == 'sum':
126
+ return np.ma.sum
127
+ elif name == 'median':
128
+ return np.ma.median
129
+ else:
130
+ return None
131
+ else:
132
+ return None
133
+
93
134
  def getkeyblock(i, addone=True) -> str:
94
135
  """
95
136
  Name/Key of a block in the dictionnary of a WolfArrayMB instance
@@ -111,6 +152,9 @@ def decodekeyblock(key, addone=True) -> int:
111
152
  return int(key[5:])
112
153
  else:
113
154
  return int(key[5:]) - 1
155
+
156
+
157
+
114
158
  class header_wolf():
115
159
  """
116
160
  Header of WolfArray
@@ -284,9 +328,9 @@ class header_wolf():
284
328
  """
285
329
  Set translation
286
330
 
287
- :param tr_x = translation along X
288
- :param tr_y = translation along Y
289
- :param tr_z = translation along Z
331
+ :param tr_x: translation along X
332
+ :param tr_y: translation along Y
333
+ :param tr_z: translation along Z
290
334
  """
291
335
  self.translx = tr_x
292
336
  self.transly = tr_y
@@ -4396,14 +4440,24 @@ class WolfArray(Element_To_Draw, header_wolf):
4396
4440
 
4397
4441
  def __del__(self):
4398
4442
  """ Destructeur de la classe """
4399
- import gc
4400
- self.delete_lists()
4401
- del self.array
4402
- if VERSION_RGB==1 : del self.rgb
4403
- del self._array3d
4404
- del self.mypal
4405
- del self.shaded
4406
- gc.collect()
4443
+ try:
4444
+ # Perform cleanup tasks safely
4445
+ self.delete_lists()
4446
+ if hasattr(self, 'array'):
4447
+ del self.array
4448
+ if VERSION_RGB == 1 and hasattr(self, 'rgb'):
4449
+ del self.rgb
4450
+ if hasattr(self, '_array3d'):
4451
+ del self._array3d
4452
+ if hasattr(self, 'mypal'):
4453
+ del self.mypal
4454
+ if hasattr(self, 'shaded'):
4455
+ del self.shaded
4456
+ # Perform garbage collection if gc is available
4457
+ import gc
4458
+ gc.collect()
4459
+ except Exception as e:
4460
+ print(f"Exception in WolfArray destructor: {e} -- Please report this issue")
4407
4461
 
4408
4462
  def extract_selection(self):
4409
4463
  """ Extract the current selection """
@@ -4802,7 +4856,7 @@ class WolfArray(Element_To_Draw, header_wolf):
4802
4856
  if reset_plot:
4803
4857
  self.reset_plot()
4804
4858
 
4805
- def export_geotif(self, outdir='', extent = ''):
4859
+ def export_geotif(self, outdir='', extent = '', EPSG:int = 31370):
4806
4860
  """
4807
4861
  Export de la matrice au format Geotiff (Lambert 72 - EPSG:31370)
4808
4862
 
@@ -4813,11 +4867,12 @@ class WolfArray(Element_To_Draw, header_wolf):
4813
4867
 
4814
4868
  :param outdir: directory
4815
4869
  :param extent: suffix to add to the filename before the extension '.tif'
4870
+ :param EPSG: EPSG code, by default 31370 (Lambert 72)
4816
4871
  """
4817
4872
  from osgeo import gdal, osr, gdalconst
4818
4873
 
4819
4874
  srs = osr.SpatialReference()
4820
- srs.ImportFromEPSG(31370)
4875
+ srs.ImportFromEPSG(EPSG)
4821
4876
 
4822
4877
  if outdir=='' and extent=='':
4823
4878
  filename = self.filename
@@ -4845,7 +4900,17 @@ class WolfArray(Element_To_Draw, header_wolf):
4845
4900
  out_ds: gdal.Dataset
4846
4901
  band: gdal.Band
4847
4902
  driver = gdal.GetDriverByName("GTiff")
4848
- out_ds = driver.Create(filename, arr.shape[0], arr.shape[1], 1, arr_type, options=['COMPRESS=LZW'])
4903
+ # bytes_per_pixel = arr.data.dtype.itemsize
4904
+ estimated_file_size = self.memory_usage #arr.shape[0] * arr.shape[1] * bytes_per_pixel
4905
+
4906
+ # Check if estimated file size exceeds 4GB
4907
+ if (estimated_file_size > 4 * 1024**3): # 4GB in bytes
4908
+ options = ['COMPRESS=LZW', 'BIGTIFF=YES']
4909
+ print('BigTIFF format will be used!')
4910
+ else:
4911
+ options = ['COMPRESS=LZW']
4912
+
4913
+ out_ds = driver.Create(filename, arr.shape[0], arr.shape[1], 1, arr_type, options=options)
4849
4914
  out_ds.SetProjection(srs.ExportToWkt())
4850
4915
 
4851
4916
 
@@ -5109,6 +5174,10 @@ class WolfArray(Element_To_Draw, header_wolf):
5109
5174
  except:
5110
5175
  logging.warning(_('Error during importing tif file'))
5111
5176
 
5177
+ # Close the raster
5178
+ raster.FlushCache()
5179
+ raster = None
5180
+
5112
5181
  def add_ops_sel(self):
5113
5182
  """
5114
5183
  Adding selection manager and operations array
@@ -6682,11 +6751,12 @@ class WolfArray(Element_To_Draw, header_wolf):
6682
6751
  if update_min_max:
6683
6752
  self.mypal.distribute_values(self.array.min(), self.array.max())
6684
6753
 
6685
- def write_all(self, newpath:str = None):
6754
+ def write_all(self, newpath:str = None, EPSG:int = 31370):
6686
6755
  """
6687
6756
  Ecriture de tous les fichiers d'un Wolf array
6688
6757
 
6689
6758
  :param newpath: new path and filename with extension -- if None, use the current filename
6759
+ :param EPSG: EPSG code for geotiff
6690
6760
  """
6691
6761
 
6692
6762
  if isinstance(newpath, Path):
@@ -6696,7 +6766,7 @@ class WolfArray(Element_To_Draw, header_wolf):
6696
6766
  self.filename = newpath
6697
6767
 
6698
6768
  if self.filename.endswith('.tif'):
6699
- self.export_geotif()
6769
+ self.export_geotif(EPSG=EPSG)
6700
6770
  elif self.filename.endswith('.npy'):
6701
6771
 
6702
6772
  writing_header = True
@@ -6730,18 +6800,26 @@ class WolfArray(Element_To_Draw, header_wolf):
6730
6800
  self.write_txt_header()
6731
6801
  self.write_array()
6732
6802
 
6733
- def rebin(self, factor:float, operation:Literal['mean', 'sum', 'min']='mean') -> None:
6803
+ def get_rebin_shape_size(self, factor:float) -> tuple[tuple[int, int], tuple[float, float]]:
6734
6804
  """
6735
- Change resolution - in place
6805
+ Return the new shape after rebinning.
6736
6806
 
6737
- :param factor: factor of resolution change -- > 1.0 : decrease resolution, < 1.0 : increase resolution
6807
+ newdx = dx * factor
6808
+ newdy = dy * factor
6809
+
6810
+ The shape is adjusted to be a multiple of the factor.
6738
6811
 
6739
- If you want to keep current data, copy the WolfArray into a new variable -> newWA = Wolfarray(mold=curWA)
6812
+ :param factor: factor of resolution change -- > 1.0 : decrease resolution, < 1.0 : increase resolution
6813
+ :type factor: float
6814
+ :return: new shape
6815
+ :rtype: Tuple[int, int]
6740
6816
  """
6741
- operation = operation.lower()
6742
- if not operation in ['sum', 'mean', 'min']:
6743
- raise ValueError("Operator not supported.")
6744
6817
 
6818
+ newdx = self.dx * float(factor)
6819
+ newdy = self.dy * float(factor)
6820
+
6821
+ newnbx = self.nbx
6822
+ newnby = self.nby
6745
6823
  if np.mod(self.nbx,factor) != 0 or np.mod(self.nby,factor) != 0 :
6746
6824
  newnbx = self.nbx
6747
6825
  newnby = self.nby
@@ -6750,8 +6828,85 @@ class WolfArray(Element_To_Draw, header_wolf):
6750
6828
  if np.mod(self.nby,factor) !=0:
6751
6829
  newnby = self.nby + factor - np.mod(self.nby,factor)
6752
6830
 
6753
- newarray = np.ma.zeros((newnbx,newnby))
6831
+ newnbx = int(newnbx / factor)
6832
+ newnby = int(newnby / factor)
6833
+
6834
+ return (newnbx, newnby), (newdx, newdy)
6835
+
6836
+ def get_rebin_header(self, factor:float) -> header_wolf:
6837
+ """
6838
+ Return a new header after rebinning.
6839
+
6840
+ :param factor: factor of resolution change -- > 1.0 : decrease resolution, < 1.0 : increase resolution
6841
+ :type factor: float
6842
+
6843
+ :return: new header
6844
+ :rtype: header_wolf
6845
+ """
6846
+
6847
+ newshape, newdx_dy = self.get_rebin_shape_size(factor)
6848
+
6849
+ newheader = self.get_header()
6850
+
6851
+ newheader.nbx = newshape[0]
6852
+ newheader.nby = newshape[1]
6853
+ newheader.dx = newdx_dy[0]
6854
+ newheader.dy = newdx_dy[1]
6855
+
6856
+ return newheader
6857
+
6858
+ def rebin(self,
6859
+ factor:float,
6860
+ operation:Literal['mean', 'sum', 'min', 'max', 'median'] ='mean',
6861
+ operation_matrix:"WolfArray"=None) -> None:
6862
+ """
6863
+ Change resolution - **in place**.
6864
+
6865
+ If you want to keep current data, copy the WolfArray into a new variable -> newWA = Wolfarray(mold=curWA).
6866
+
6867
+ :param factor: factor of resolution change -- > 1.0 : decrease resolution, < 1.0 : increase resolution
6868
+ :type factor: float
6869
+ :param operation: operation to apply on the blocks ('mean', 'sum', 'min', 'max', 'median')
6870
+ :type operation: str, Rebin_Ops
6871
+ :param operation_matrix: operation matrix to apply on the blocks -- see the Enum "Rebin_Ops" for more infos. The matrix must have the same shape as the new array
6872
+ :type operation_matrix: WolfArray
6873
+
6874
+ """
6875
+
6876
+ if operation_matrix is not None:
6877
+ tmp_header = self.get_rebin_header(factor)
6878
+ if not operation_matrix.is_like(tmp_header):
6879
+ logging.error(_("The operation matrix must have the same shape as the new array"))
6880
+ logging.info(_("You can use the get_rebin_header method to get the new header if you don't know it"))
6881
+ return
6882
+
6883
+ logging.info(_("Operation matrix detected"))
6884
+ logging.info(_("The operation matrix will be used to apply the operation on the blocks"))
6885
+ else:
6886
+
6887
+ operation = Rebin_Ops.get_ops(operation)
6888
+
6889
+ if operation is None:
6890
+ logging.error(_("Operator not supported -- Must be a string in ['sum', 'mean', 'min', 'max', 'median'] or a Rebin_Ops Enum"))
6891
+ return
6892
+
6893
+ if not callable(operation):
6894
+ logging.error(_("Operator not supported -- Must be a string in ['sum', 'mean', 'min', 'max', 'median'] or a Rebin_Ops Enum"))
6895
+
6896
+
6897
+ if np.mod(self.nbx,factor) != 0 or np.mod(self.nby,factor) != 0 :
6898
+ # The shape is adjusted to be a multiple of the factor.
6899
+ # Fill the array with nullvalue
6900
+ newnbx = self.nbx
6901
+ newnby = self.nby
6902
+ if np.mod(self.nbx,factor) !=0:
6903
+ newnbx = int(self.nbx + factor - np.mod(self.nbx,factor))
6904
+ if np.mod(self.nby,factor) !=0:
6905
+ newnby = int(self.nby + factor - np.mod(self.nby,factor))
6906
+
6907
+ newarray = np.ma.ones((newnbx,newnby), dtype = self.dtype) * self.nullvalue
6754
6908
  newarray[:self.nbx,:self.nby] = self.array
6909
+ newarray.mask[:self.nbx,:self.nby] = self.array.mask
6755
6910
  self.array = newarray
6756
6911
 
6757
6912
  self.nbx = newnbx
@@ -6765,20 +6920,41 @@ class WolfArray(Element_To_Draw, header_wolf):
6765
6920
  new_shape = (self.nbx, self.nby)
6766
6921
 
6767
6922
  if factor>1.:
6768
- compression_pairs = [(d, c // d) for d, c in zip(new_shape,
6769
- self.array.shape)]
6770
- flattened = [l for p in compression_pairs for l in p]
6771
- self.array = self.array.reshape(flattened)
6772
- for i in range(len(new_shape)):
6773
- op = getattr(self.array, operation)
6774
- self.array = np.float32(op(-1 * (i + 1)))
6923
+ if operation_matrix is not None:
6924
+ # Reshape the input array to split it into blocks of size f x f
6925
+ reshaped_a = self.array.reshape(new_shape[0], int(factor), new_shape[1], int(factor))
6926
+
6927
+ # Swap axes to make blocks as separate dimensions
6928
+ reshaped_a = reshaped_a.swapaxes(1, 2)
6929
+
6930
+ # Initialize the output matrix
6931
+ self.array = ma.masked_array(np.ones((new_shape[0], new_shape[1]), dtype= self.dtype) * self.nullvalue, dtype= self.dtype)
6932
+
6933
+ # Check the dtype of the newly initialized array
6934
+ assert self.array.dtype == self.dtype, _('Bad dtype')
6935
+
6936
+ # Vectorized operations
6937
+ for op_idx, operation in enumerate(Rebin_Ops.get_numpy_ops()):
6938
+ mask = (operation_matrix.array == op_idx)
6939
+ if np.any(mask):
6940
+ block_results = operation(reshaped_a, axis=(2, 3))
6941
+ self.array[mask] = block_results[mask]
6942
+
6943
+ else:
6944
+ compression_pairs = [(d, c // d) for d, c in zip(new_shape,
6945
+ self.array.shape)]
6946
+ flattened = [l for p in compression_pairs for l in p]
6947
+ self.array = operation(self.array.reshape(flattened), axis=(1, 3)).astype(self.dtype)
6948
+
6949
+ self.set_nullvalue_in_mask()
6775
6950
  else:
6776
6951
  self.array = np.kron(self.array, np.ones((int(1/factor), int(1/factor)), dtype=self.array.dtype))
6777
6952
 
6778
- self.mask_reset()
6779
-
6780
6953
  self.count()
6781
6954
 
6955
+ # rebin must not change the type of the array
6956
+ assert self.array.dtype == self.dtype, _('Bad dtype')
6957
+
6782
6958
  def read_txt_header(self):
6783
6959
  """
6784
6960
  Read header from txt file
@@ -8001,6 +8177,54 @@ class WolfArray(Element_To_Draw, header_wolf):
8001
8177
 
8002
8178
  self.reset_plot()
8003
8179
 
8180
+ @classmethod
8181
+ def from_other_epsg_coo(cls,
8182
+ input_raster_path:str,
8183
+ input_srs='EPSG:3812',
8184
+ output_srs='EPSG:31370',
8185
+ resampling_method=gdal.GRA_Bilinear,
8186
+ xRes:float=0.5, yRes:float=0.5):
8187
+ """
8188
+ Reprojects and resamples a raster file from an other EPSG coordinates and return it as a WolfArray.
8189
+
8190
+ :param input_raster_path: The path to the input raster file.
8191
+ :type input_raster_path: str
8192
+ :param input_srs: The input spatial reference system (SRS) in the format 'EPSG:XXXX'. Defaults to Lambert 2008 'EPSG:3812'.
8193
+ :type input_srs: str
8194
+ :param output_srs: The output spatial reference system (SRS) in the format 'EPSG:XXXX'. Defaults to Belgian Lambert 72 'EPSG:31370'.
8195
+ :type output_srs: str
8196
+ :param resampling_method: The resampling method to use. Defaults to gdal.GRA_Bilinear. Resampling method can be chosen among the gdal GRA_*
8197
+ constants (gdal.GRA_Average; gdal.GRA_Bilinear; gdal.GRA_Cubic; gdal.GRA_CubicSpline;
8198
+ gdal.GRA_Lanczos; gdal.GRA_Mode; gdal.GRA_NearestNeighbour)
8199
+ :type resampling_method: int
8200
+ :param xRes: The desired output resolution in the x direction. Defaults to 0.5.
8201
+ :type xRes (float): float
8202
+ :param yRes: The desired output resolution in the y direction. Defaults to 0.5.
8203
+ :type yRes (float): float
8204
+
8205
+ :raises AssertionError: If the input or output raster file is not a GeoTIFF file.
8206
+ :raises RuntimeError: If the input raster file cannot be opened.
8207
+ :raises PermissionError: If there is a permission error while trying to delete the output raster file.
8208
+ :raises Exception: If an unexpected error occurs while trying to delete the output raster file.
8209
+ :raises RuntimeError: If the reprojection fails for the input raster file.
8210
+
8211
+ :return: WolfArray
8212
+ """
8213
+
8214
+ #sanitize input
8215
+ input_raster_path = str(input_raster_path)
8216
+ input_srs = str(input_srs)
8217
+ output_srs = str(output_srs)
8218
+
8219
+ assert resampling_method in [gdal.GRA_Average, gdal.GRA_Bilinear, gdal.GRA_Cubic, gdal.GRA_CubicSpline, gdal.GRA_Lanczos, gdal.GRA_Mode, gdal.GRA_NearestNeighbour], "Invalid resampling method"
8220
+
8221
+ # Define temporary files
8222
+ with tempfile.TemporaryDirectory() as temp_dir:
8223
+ output_raster_path = os.path.join(temp_dir, "Array_72.tif")
8224
+ reproject_and_resample_raster(input_raster_path, output_raster_path, input_srs, output_srs, resampling_method, xRes, yRes)
8225
+ Array3 = WolfArray(output_raster_path, nullvalue=-9999)
8226
+ return Array3
8227
+
8004
8228
  class WolfArrayMB(WolfArray):
8005
8229
  """
8006
8230
  Matrice multiblocks
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: wolfhece
3
- Version: 2.1.46
3
+ Version: 2.1.48
4
4
  Author-email: Pierre Archambeau <pierre.archambeau@uliege.be>
5
5
  License: Copyright (c) 2024 University of Liege. All rights reserved.
6
6
  Project-URL: Homepage, https://uee.uliege.be/hece
@@ -13,14 +13,14 @@ Classifier: Topic :: Scientific/Engineering :: Physics
13
13
  Requires-Python: <3.11,>=3.10
14
14
  Description-Content-Type: text/markdown
15
15
  Requires-Dist: wxpython
16
- Requires-Dist: colorlog
16
+ Requires-Dist: colorlog ==6.7.*
17
17
  Requires-Dist: intel-fortran-rt
18
18
  Requires-Dist: scikit-learn
19
19
  Requires-Dist: cryptography
20
- Requires-Dist: jax
20
+ Requires-Dist: jax ==0.4.30
21
21
  Requires-Dist: triangle
22
- Requires-Dist: numpy
23
- Requires-Dist: pyopengl
22
+ Requires-Dist: numpy ==1.23.*
23
+ Requires-Dist: pyopengl ==3.1.*
24
24
  Requires-Dist: pandas
25
25
  Requires-Dist: geopandas
26
26
  Requires-Dist: scipy
@@ -33,7 +33,7 @@ Requires-Dist: graphviz
33
33
  Requires-Dist: beautifulsoup4
34
34
  Requires-Dist: requests
35
35
  Requires-Dist: notebook
36
- Requires-Dist: matplotlib
36
+ Requires-Dist: matplotlib ==3.6.*
37
37
  Requires-Dist: mkl
38
38
  Requires-Dist: python-gettext
39
39
  Requires-Dist: shapely
@@ -49,10 +49,10 @@ Requires-Dist: python-docx
49
49
  Requires-Dist: pygltflib
50
50
  Requires-Dist: ezdxf
51
51
  Requires-Dist: pyvista
52
- Requires-Dist: tqdm
52
+ Requires-Dist: tqdm ==4.64.*
53
53
  Requires-Dist: osmnx
54
54
  Requires-Dist: tifffile
55
- Requires-Dist: numba
55
+ Requires-Dist: numba ==0.58.*
56
56
  Requires-Dist: xmltodict
57
57
  Requires-Dist: opencv-python
58
58
  Requires-Dist: xarray
@@ -1,3 +1,4 @@
1
+ wolfhece/Coordinates_operations.py,sha256=YyWlAwKManb-ReQrmP37rEXxehunUCihmkeDYX6qTAQ,8037
1
2
  wolfhece/CpGrid.py,sha256=ke4n1khTUoed2asJl1GR25PsEkI4TpiBDCo4u0aSo9M,10658
2
3
  wolfhece/GraphNotebook.py,sha256=V1_Ak4F_hoIpKm2GAakuyKOALhujjIXB5FwzFHtM5-8,28021
3
4
  wolfhece/GraphProfile.py,sha256=OCgJo0YFFBI6H1z-5egJsOOoWF_iziiza0-bbPejNMc,69656
@@ -47,7 +48,7 @@ wolfhece/pywalous.py,sha256=yRaWJjKckXef1d9D5devP0yFHC9uc6kRV4G5x9PNq9k,18972
47
48
  wolfhece/rain_SPWMI.py,sha256=qCfcmF7LajloOaCwnTrrSMzyME03YyilmRUOqrPrv3U,13846
48
49
  wolfhece/textpillow.py,sha256=map7HsGYML_o5NHRdFg2s_TVQed_lDnpYNDv27MM0Vw,14130
49
50
  wolfhece/tools_mpl.py,sha256=gQ3Jg1iuZiecmMqa5Eli2ZLSkttu68VXL8YmMDBaEYU,564
50
- wolfhece/wolf_array.py,sha256=8UwY94icIbz2BGHtBBdjoia0ijlriJsLcvuJ0Ji-DTg,358915
51
+ wolfhece/wolf_array.py,sha256=cWkTaA4nY9I8-FLNlXBwn0xkvaN1EXw6M9NYUYnTqf8,368492
51
52
  wolfhece/wolf_hist.py,sha256=7jeVrgSkM3ErJO6SRMH_PGzfLjIdw8vTy87kesldggk,3582
52
53
  wolfhece/wolf_texture.py,sha256=DS5eobLxrq9ljyebYfpMSQPn8shkUAZZVfqrOKN_QUU,16951
53
54
  wolfhece/wolf_tiles.py,sha256=2Ho2I20rHRY81KXxjgLOYISdF4OkJ2d6omeY4shDoGI,10386
@@ -71,7 +72,7 @@ wolfhece/apps/check_install.py,sha256=SG024u18G7VRLKynbp7DKD1jImtHwuWwN4bJWHm-YH
71
72
  wolfhece/apps/curvedigitizer.py,sha256=_hRR2PWow7PU7rTHIbc6ykZ08tCXcK9uy7RFrb4EKkE,5196
72
73
  wolfhece/apps/isocurrent.py,sha256=MuwTodHxdc6PrqNpphR2ntYf1NLL2n9klTPndGrOHDQ,4109
73
74
  wolfhece/apps/splashscreen.py,sha256=SrustmIQeXnsiD-92OzjdGhBi-S7c_j-cSvuX4T6rtg,2929
74
- wolfhece/apps/version.py,sha256=NcO4o6jWbzNyEQy_q5jp-nVRYwCMSE9siAWq3vRNszg,388
75
+ wolfhece/apps/version.py,sha256=1j6FkD-S6lKIU8cwsITdvdCE6HZVOxDK9aVtSrdT5Vw,388
75
76
  wolfhece/apps/wolf.py,sha256=mM6Tyi4DlKQILmO49cDUCip9fYVy-hLXkY3YhZgIeUQ,591
76
77
  wolfhece/apps/wolf2D.py,sha256=yPQGee7fsegoQ8GfWKrWEjX1Az_ApL-UWlBiqPvaIyY,565
77
78
  wolfhece/apps/wolf_logo.bmp,sha256=ruJ4MA51CpGO_AYUp_dB4SWKHelvhOvd7Q8NrVOjDJk,3126
@@ -277,8 +278,8 @@ wolfhece/ui/wolf_multiselection_collapsiblepane.py,sha256=8PlMYrb_8jI8h9F0_EagpM
277
278
  wolfhece/ui/wolf_times_selection_comparison_models.py,sha256=ORy7fz4dcp691qKzaOZHrRLZ0uXNhL-LIHxmpDGL6BI,5007
278
279
  wolfhece/wintab/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
279
280
  wolfhece/wintab/wintab.py,sha256=8A-JNONV6ujgsgG3lM5Uw-pVgglPATwKs86oBzzljoc,7179
280
- wolfhece-2.1.46.dist-info/METADATA,sha256=Me3fak5XA8FOqijhfnRwcDvv4O8CNe51c17L0oHxDsY,2488
281
- wolfhece-2.1.46.dist-info/WHEEL,sha256=R0nc6qTxuoLk7ShA2_Y-UWkN8ZdfDBG2B6Eqpz2WXbs,91
282
- wolfhece-2.1.46.dist-info/entry_points.txt,sha256=Q5JuIWV4odeIJI3qc6fV9MwRoz0ezqPVlFC1Ppm_vdQ,395
283
- wolfhece-2.1.46.dist-info/top_level.txt,sha256=EfqZXMVCn7eILUzx9xsEu2oBbSo9liWPFWjIHik0iCI,9
284
- wolfhece-2.1.46.dist-info/RECORD,,
281
+ wolfhece-2.1.48.dist-info/METADATA,sha256=2S8MGX7bCP41sSY7a9scSmcsfKj2w8uiaMzIY5bIY9k,2548
282
+ wolfhece-2.1.48.dist-info/WHEEL,sha256=R0nc6qTxuoLk7ShA2_Y-UWkN8ZdfDBG2B6Eqpz2WXbs,91
283
+ wolfhece-2.1.48.dist-info/entry_points.txt,sha256=Q5JuIWV4odeIJI3qc6fV9MwRoz0ezqPVlFC1Ppm_vdQ,395
284
+ wolfhece-2.1.48.dist-info/top_level.txt,sha256=EfqZXMVCn7eILUzx9xsEu2oBbSo9liWPFWjIHik0iCI,9
285
+ wolfhece-2.1.48.dist-info/RECORD,,