cht_utils 2.0.0__tar.gz
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.
- cht_utils-2.0.0/LICENSE +21 -0
- cht_utils-2.0.0/PKG-INFO +30 -0
- cht_utils-2.0.0/cht_utils/__init__.py +28 -0
- cht_utils-2.0.0/cht_utils/cog/__init__.py +6 -0
- cht_utils-2.0.0/cht_utils/cog/geotiff_to_cog.py +79 -0
- cht_utils-2.0.0/cht_utils/cog/netcdf_to_cog.py +85 -0
- cht_utils-2.0.0/cht_utils/cog/xyz_to_cog.py +86 -0
- cht_utils-2.0.0/cht_utils/colors/__init__.py +6 -0
- cht_utils-2.0.0/cht_utils/colors/colors.py +117 -0
- cht_utils-2.0.0/cht_utils/fileio/__init__.py +21 -0
- cht_utils-2.0.0/cht_utils/fileio/deltares_ini.py +326 -0
- cht_utils-2.0.0/cht_utils/fileio/json_js.py +72 -0
- cht_utils-2.0.0/cht_utils/fileio/pli_file.py +233 -0
- cht_utils-2.0.0/cht_utils/fileio/tekal.py +234 -0
- cht_utils-2.0.0/cht_utils/fileio/xml.py +184 -0
- cht_utils-2.0.0/cht_utils/fileio/yaml.py +39 -0
- cht_utils-2.0.0/cht_utils/fileops/__init__.py +25 -0
- cht_utils-2.0.0/cht_utils/fileops/fileops.py +344 -0
- cht_utils-2.0.0/cht_utils/interpolation/__init__.py +5 -0
- cht_utils-2.0.0/cht_utils/interpolation/interpolation.py +152 -0
- cht_utils-2.0.0/cht_utils/maps/__init__.py +2 -0
- cht_utils-2.0.0/cht_utils/maps/fileops.py +191 -0
- cht_utils-2.0.0/cht_utils/maps/flood_map.py +1231 -0
- cht_utils-2.0.0/cht_utils/maps/topobathy_map.py +463 -0
- cht_utils-2.0.0/cht_utils/maps/utils.py +700 -0
- cht_utils-2.0.0/cht_utils/physics/__init__.py +8 -0
- cht_utils-2.0.0/cht_utils/physics/deshoal.py +63 -0
- cht_utils-2.0.0/cht_utils/physics/disper.py +91 -0
- cht_utils-2.0.0/cht_utils/physics/runup_vo21.py +229 -0
- cht_utils-2.0.0/cht_utils/physics/waves.py +59 -0
- cht_utils-2.0.0/cht_utils/probabilistic/__init__.py +5 -0
- cht_utils-2.0.0/cht_utils/probabilistic/prob_maps.py +263 -0
- cht_utils-2.0.0/cht_utils/remote/__init__.py +4 -0
- cht_utils-2.0.0/cht_utils/remote/s3.py +380 -0
- cht_utils-2.0.0/cht_utils/remote/sftp.py +192 -0
- cht_utils-2.0.0/cht_utils.egg-info/PKG-INFO +30 -0
- cht_utils-2.0.0/cht_utils.egg-info/SOURCES.txt +49 -0
- cht_utils-2.0.0/cht_utils.egg-info/dependency_links.txt +1 -0
- cht_utils-2.0.0/cht_utils.egg-info/requires.txt +19 -0
- cht_utils-2.0.0/cht_utils.egg-info/top_level.txt +1 -0
- cht_utils-2.0.0/cht_utils.egg-info/zip-safe +1 -0
- cht_utils-2.0.0/pyproject.toml +59 -0
- cht_utils-2.0.0/setup.cfg +4 -0
- cht_utils-2.0.0/tests/test_cog.py +30 -0
- cht_utils-2.0.0/tests/test_colors.py +56 -0
- cht_utils-2.0.0/tests/test_fileio.py +126 -0
- cht_utils-2.0.0/tests/test_fileops.py +320 -0
- cht_utils-2.0.0/tests/test_interpolation.py +89 -0
- cht_utils-2.0.0/tests/test_physics.py +133 -0
- cht_utils-2.0.0/tests/test_probabilistic.py +18 -0
- cht_utils-2.0.0/tests/test_remote.py +29 -0
cht_utils-2.0.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 maartenvanormondt
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
cht_utils-2.0.0/PKG-INFO
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: cht_utils
|
|
3
|
+
Version: 2.0.0
|
|
4
|
+
Summary: Utility functions for the Coastal Hazards Toolkit
|
|
5
|
+
Author-email: Maarten van Ormondt <maarten.vanormondt@deltares-usa.us>
|
|
6
|
+
License: MIT
|
|
7
|
+
Classifier: Intended Audience :: Science/Research
|
|
8
|
+
Classifier: Topic :: Scientific/Engineering :: Hydrology
|
|
9
|
+
Requires-Python: >=3.10
|
|
10
|
+
Description-Content-Type: text/markdown
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Requires-Dist: boto3
|
|
13
|
+
Requires-Dist: contextily
|
|
14
|
+
Requires-Dist: geopandas
|
|
15
|
+
Requires-Dist: numpy
|
|
16
|
+
Requires-Dist: matplotlib
|
|
17
|
+
Requires-Dist: pandas
|
|
18
|
+
Requires-Dist: paramiko
|
|
19
|
+
Requires-Dist: pillow
|
|
20
|
+
Requires-Dist: pyproj
|
|
21
|
+
Requires-Dist: pyyaml
|
|
22
|
+
Requires-Dist: scipy
|
|
23
|
+
Requires-Dist: shapely
|
|
24
|
+
Requires-Dist: rioxarray
|
|
25
|
+
Requires-Dist: rasterio
|
|
26
|
+
Requires-Dist: xarray
|
|
27
|
+
Provides-Extra: tests
|
|
28
|
+
Requires-Dist: pytest; extra == "tests"
|
|
29
|
+
Requires-Dist: pytest-cov; extra == "tests"
|
|
30
|
+
Dynamic: license-file
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
__version__ = "2.0.0"
|
|
2
|
+
|
|
3
|
+
"""CHT utilities — common tools for the Deltares Coastal Hazard Toolkit.
|
|
4
|
+
|
|
5
|
+
Subpackages
|
|
6
|
+
-----------
|
|
7
|
+
fileio
|
|
8
|
+
File I/O for Deltares formats (INI, Tekal, PLI/POL), YAML, JSON-JS, XML.
|
|
9
|
+
fileops
|
|
10
|
+
File and directory operations (copy, move, delete, list).
|
|
11
|
+
cog
|
|
12
|
+
Cloud-Optimized GeoTIFF conversion (from GeoTIFF, NetCDF, XYZ).
|
|
13
|
+
physics
|
|
14
|
+
Coastal and wave physics (dispersion, shoaling, runup, wave decomposition).
|
|
15
|
+
remote
|
|
16
|
+
Remote file transfer (AWS S3, SSH/SFTP).
|
|
17
|
+
probabilistic
|
|
18
|
+
Ensemble post-processing and probability maps.
|
|
19
|
+
maps
|
|
20
|
+
Floodmap and Topography map visualization.
|
|
21
|
+
|
|
22
|
+
Top-level modules
|
|
23
|
+
-----------------
|
|
24
|
+
colors
|
|
25
|
+
Colormap registration, rendering, and conversion.
|
|
26
|
+
interpolation
|
|
27
|
+
2-D interpolation on regular and unstructured grids.
|
|
28
|
+
"""
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
"""Cloud-Optimized GeoTIFF (COG) conversion utilities."""
|
|
2
|
+
|
|
3
|
+
from cht_utils.cog.geotiff_to_cog import geotiff_to_cog as geotiff_to_cog
|
|
4
|
+
from cht_utils.cog.geotiff_to_cog import is_cog as is_cog
|
|
5
|
+
from cht_utils.cog.netcdf_to_cog import netcdf_to_cog as netcdf_to_cog
|
|
6
|
+
from cht_utils.cog.xyz_to_cog import xyz_to_cog as xyz_to_cog
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
"""Convert GeoTIFF files to Cloud-Optimized GeoTIFF (COG) format."""
|
|
2
|
+
|
|
3
|
+
import shutil
|
|
4
|
+
|
|
5
|
+
import rasterio
|
|
6
|
+
from rasterio.shutil import copy as rio_copy
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def is_cog(tif_path: str) -> bool:
|
|
10
|
+
"""Check if a GeoTIFF is Cloud-Optimized.
|
|
11
|
+
|
|
12
|
+
Verifies that the file is tiled and has overviews.
|
|
13
|
+
|
|
14
|
+
Parameters
|
|
15
|
+
----------
|
|
16
|
+
tif_path : str
|
|
17
|
+
Path to the GeoTIFF file.
|
|
18
|
+
|
|
19
|
+
Returns
|
|
20
|
+
-------
|
|
21
|
+
bool
|
|
22
|
+
``True`` if the file is a COG.
|
|
23
|
+
"""
|
|
24
|
+
try:
|
|
25
|
+
with rasterio.open(tif_path) as src:
|
|
26
|
+
if not src.is_tiled:
|
|
27
|
+
return False
|
|
28
|
+
overviews = src.overviews(1)
|
|
29
|
+
return len(overviews) > 0
|
|
30
|
+
except Exception as e:
|
|
31
|
+
print(f"Error checking COG: {e}")
|
|
32
|
+
return False
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def geotiff_to_cog(
|
|
36
|
+
geotiff_path: str,
|
|
37
|
+
output_cog_path: str,
|
|
38
|
+
resampling: str = "average",
|
|
39
|
+
) -> bool:
|
|
40
|
+
"""Convert a GeoTIFF to Cloud-Optimized GeoTIFF.
|
|
41
|
+
|
|
42
|
+
If the input is already a COG, it is simply copied.
|
|
43
|
+
|
|
44
|
+
Parameters
|
|
45
|
+
----------
|
|
46
|
+
geotiff_path : str
|
|
47
|
+
Path to the input GeoTIFF.
|
|
48
|
+
output_cog_path : str
|
|
49
|
+
Path for the output COG.
|
|
50
|
+
resampling : str
|
|
51
|
+
Resampling method for overviews (default: ``"average"``).
|
|
52
|
+
|
|
53
|
+
Returns
|
|
54
|
+
-------
|
|
55
|
+
bool
|
|
56
|
+
``True`` on success, ``False`` on error.
|
|
57
|
+
"""
|
|
58
|
+
try:
|
|
59
|
+
if is_cog(geotiff_path):
|
|
60
|
+
print(f"{geotiff_path} is already a COG! Copying to {output_cog_path}")
|
|
61
|
+
shutil.copy(geotiff_path, output_cog_path)
|
|
62
|
+
return True
|
|
63
|
+
|
|
64
|
+
with rasterio.open(geotiff_path) as src:
|
|
65
|
+
rio_copy(
|
|
66
|
+
src,
|
|
67
|
+
output_cog_path,
|
|
68
|
+
driver="COG",
|
|
69
|
+
compress="deflate",
|
|
70
|
+
blocksize=512,
|
|
71
|
+
overview_resampling=resampling,
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
print(f"COG saved to: {output_cog_path}")
|
|
75
|
+
return True
|
|
76
|
+
|
|
77
|
+
except Exception as e:
|
|
78
|
+
print(f"Error during conversion: {e}")
|
|
79
|
+
return False
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
"""Convert NetCDF variables to Cloud-Optimized GeoTIFF (COG) format."""
|
|
2
|
+
|
|
3
|
+
from typing import Optional
|
|
4
|
+
|
|
5
|
+
import xarray as xr
|
|
6
|
+
from pyproj import CRS
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def netcdf_to_cog(
|
|
10
|
+
netcdf_path: str,
|
|
11
|
+
variable_name: str,
|
|
12
|
+
output_cog_path: str,
|
|
13
|
+
time_index: Optional[int] = None,
|
|
14
|
+
) -> bool:
|
|
15
|
+
"""Convert a NetCDF variable to a Cloud-Optimized GeoTIFF.
|
|
16
|
+
|
|
17
|
+
Assumes EPSG:4326 if no CRS information is found in the dataset.
|
|
18
|
+
|
|
19
|
+
Parameters
|
|
20
|
+
----------
|
|
21
|
+
netcdf_path : str
|
|
22
|
+
Path to the NetCDF file.
|
|
23
|
+
variable_name : str
|
|
24
|
+
Name of the variable to extract.
|
|
25
|
+
output_cog_path : str
|
|
26
|
+
Output path for the COG.
|
|
27
|
+
time_index : int or None
|
|
28
|
+
Optional time dimension index to select.
|
|
29
|
+
|
|
30
|
+
Returns
|
|
31
|
+
-------
|
|
32
|
+
bool
|
|
33
|
+
``True`` on success, ``False`` on error.
|
|
34
|
+
"""
|
|
35
|
+
try:
|
|
36
|
+
ds = xr.open_dataset(netcdf_path)
|
|
37
|
+
|
|
38
|
+
if variable_name not in ds:
|
|
39
|
+
raise ValueError(
|
|
40
|
+
f"Variable '{variable_name}' not found in the NetCDF file."
|
|
41
|
+
)
|
|
42
|
+
da = ds[variable_name]
|
|
43
|
+
|
|
44
|
+
if "time" in da.dims and time_index is not None:
|
|
45
|
+
da = da.isel(time=time_index)
|
|
46
|
+
|
|
47
|
+
crs = _get_crs(ds)
|
|
48
|
+
crs_str = f"EPSG:{crs.to_epsg()}"
|
|
49
|
+
|
|
50
|
+
if crs.is_projected:
|
|
51
|
+
da.rio.set_spatial_dims(x_dim="x", y_dim="y", inplace=True)
|
|
52
|
+
else:
|
|
53
|
+
da.rio.set_spatial_dims(x_dim="lon", y_dim="lat", inplace=True)
|
|
54
|
+
|
|
55
|
+
da.rio.write_crs(crs_str, inplace=True)
|
|
56
|
+
da.rio.to_raster(
|
|
57
|
+
output_cog_path,
|
|
58
|
+
driver="COG",
|
|
59
|
+
compress="deflate",
|
|
60
|
+
blocksize=512,
|
|
61
|
+
overview_resampling="average",
|
|
62
|
+
dtype=da.dtype,
|
|
63
|
+
)
|
|
64
|
+
print(f"COG saved to: {output_cog_path}")
|
|
65
|
+
return True
|
|
66
|
+
|
|
67
|
+
except Exception as e:
|
|
68
|
+
print(f"Error during conversion: {e}")
|
|
69
|
+
return False
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def _get_crs(ds: xr.Dataset) -> CRS:
|
|
73
|
+
"""Extract CRS from a dataset, defaulting to EPSG:4326."""
|
|
74
|
+
if "crs" not in ds:
|
|
75
|
+
return CRS.from_epsg(4326)
|
|
76
|
+
|
|
77
|
+
for attr_name in ("crs_wkt", "spatial_ref"):
|
|
78
|
+
wkt = ds["crs"].attrs.get(attr_name)
|
|
79
|
+
if wkt:
|
|
80
|
+
crs = CRS.from_wkt(wkt)
|
|
81
|
+
if crs.to_epsg() is None and "NAD83" in crs.name:
|
|
82
|
+
return CRS.from_epsg(4269)
|
|
83
|
+
return crs
|
|
84
|
+
|
|
85
|
+
return CRS.from_epsg(4326)
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
"""Convert XYZ point-cloud data to Cloud-Optimized GeoTIFF (COG) format."""
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
from typing import Union
|
|
5
|
+
|
|
6
|
+
import numpy as np
|
|
7
|
+
import pandas as pd
|
|
8
|
+
import xarray as xr
|
|
9
|
+
from pyproj import CRS
|
|
10
|
+
from rasterio.enums import Resampling
|
|
11
|
+
from rasterio.transform import from_origin
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def xyz_to_cog(
|
|
15
|
+
xyz_path: str,
|
|
16
|
+
output_cog_path: str,
|
|
17
|
+
crs: Union[int, CRS] = 4326,
|
|
18
|
+
) -> bool:
|
|
19
|
+
"""Grid scattered XYZ data and write as a COG.
|
|
20
|
+
|
|
21
|
+
Parameters
|
|
22
|
+
----------
|
|
23
|
+
xyz_path : str
|
|
24
|
+
Path to a whitespace-separated XYZ file (columns: x, y, z).
|
|
25
|
+
output_cog_path : str
|
|
26
|
+
Output COG file path.
|
|
27
|
+
crs : int or pyproj.CRS
|
|
28
|
+
Coordinate reference system (default: EPSG:4326).
|
|
29
|
+
|
|
30
|
+
Returns
|
|
31
|
+
-------
|
|
32
|
+
bool
|
|
33
|
+
``True`` on success, ``False`` on error.
|
|
34
|
+
"""
|
|
35
|
+
try:
|
|
36
|
+
df = pd.read_csv(xyz_path, sep=r"\s+", names=["x", "y", "z"])
|
|
37
|
+
|
|
38
|
+
x_unique = np.sort(df["x"].unique())
|
|
39
|
+
y_unique = np.sort(df["y"].unique())
|
|
40
|
+
|
|
41
|
+
dx = np.round(np.min(np.diff(x_unique)), 6)
|
|
42
|
+
dy = np.round(np.min(np.diff(y_unique)), 6)
|
|
43
|
+
|
|
44
|
+
z_grid = np.full((len(y_unique), len(x_unique)), np.nan)
|
|
45
|
+
x_to_idx = {x: i for i, x in enumerate(x_unique)}
|
|
46
|
+
y_to_idx = {y: i for i, y in enumerate(y_unique)}
|
|
47
|
+
|
|
48
|
+
for row in df.itertuples(index=False):
|
|
49
|
+
z_grid[y_to_idx[row.y], x_to_idx[row.x]] = row.z
|
|
50
|
+
|
|
51
|
+
da = xr.DataArray(
|
|
52
|
+
data=z_grid,
|
|
53
|
+
dims=["y", "x"],
|
|
54
|
+
coords={"x": x_unique, "y": y_unique},
|
|
55
|
+
name="topography",
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
if y_unique[0] > y_unique[-1]:
|
|
59
|
+
da = da.sortby("y")
|
|
60
|
+
|
|
61
|
+
transform = from_origin(x_unique[0] - dx / 2, y_unique[-1] + dy / 2, dx, dy)
|
|
62
|
+
da.rio.write_transform(transform, inplace=True)
|
|
63
|
+
|
|
64
|
+
if isinstance(crs, int):
|
|
65
|
+
crs_string = f"EPSG:{crs}"
|
|
66
|
+
elif isinstance(crs, CRS):
|
|
67
|
+
crs_string = f"EPSG:{crs.to_epsg()}"
|
|
68
|
+
else:
|
|
69
|
+
crs_string = str(crs)
|
|
70
|
+
|
|
71
|
+
da.rio.write_crs(crs_string, inplace=True)
|
|
72
|
+
da.rio.to_raster(
|
|
73
|
+
output_cog_path,
|
|
74
|
+
driver="COG",
|
|
75
|
+
compress="deflate",
|
|
76
|
+
dtype="float32",
|
|
77
|
+
nodata=np.nan,
|
|
78
|
+
resampling=Resampling.average,
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
print(f"Saved to: {os.path.abspath(output_cog_path)}")
|
|
82
|
+
return True
|
|
83
|
+
|
|
84
|
+
except Exception as e:
|
|
85
|
+
print(f"Error: {e}")
|
|
86
|
+
return False
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
"""Colormap utilities for reading, registering, and rendering colormaps."""
|
|
2
|
+
|
|
3
|
+
from cht_utils.colors.colors import cm2png as cm2png
|
|
4
|
+
from cht_utils.colors.colors import read_color_maps as read_color_maps
|
|
5
|
+
from cht_utils.colors.colors import read_colormap as read_colormap
|
|
6
|
+
from cht_utils.colors.colors import rgb2hex as rgb2hex
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
"""Colormap utilities for reading, registering, and rendering colormaps."""
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
from typing import List, Tuple
|
|
5
|
+
|
|
6
|
+
import matplotlib as mpl
|
|
7
|
+
import matplotlib.pyplot as plt
|
|
8
|
+
import numpy as np
|
|
9
|
+
import pandas as pd
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def read_color_maps(path_name: str) -> List[str]:
|
|
13
|
+
"""Read all colormaps from a folder and register them with matplotlib.
|
|
14
|
+
|
|
15
|
+
Each ``.txt`` file in *path_name* is expected to contain whitespace-separated
|
|
16
|
+
RGB values (one row per colour, values in 0-1 range).
|
|
17
|
+
|
|
18
|
+
Parameters
|
|
19
|
+
----------
|
|
20
|
+
path_name : str
|
|
21
|
+
Directory containing colormap text files.
|
|
22
|
+
|
|
23
|
+
Returns
|
|
24
|
+
-------
|
|
25
|
+
List[str]
|
|
26
|
+
Full list of registered matplotlib colormap names.
|
|
27
|
+
"""
|
|
28
|
+
for file in os.listdir(path_name):
|
|
29
|
+
if file.endswith(".txt"):
|
|
30
|
+
name = os.path.splitext(file)[0]
|
|
31
|
+
rgb = read_colormap(os.path.join(path_name, file))
|
|
32
|
+
cmap = mpl.colors.ListedColormap(rgb, name=name)
|
|
33
|
+
mpl.colormaps.register(cmap=cmap)
|
|
34
|
+
return plt.colormaps()
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def cm2png(
|
|
38
|
+
cmap: mpl.colors.Colormap,
|
|
39
|
+
file_name: str = "colorbar.png",
|
|
40
|
+
orientation: str = "horizontal",
|
|
41
|
+
vmin: float = 0.0,
|
|
42
|
+
vmax: float = 1.0,
|
|
43
|
+
legend_title: str = "",
|
|
44
|
+
legend_label: str = "",
|
|
45
|
+
units: str = "",
|
|
46
|
+
unit_string: str = "",
|
|
47
|
+
decimals: int = -1,
|
|
48
|
+
) -> None:
|
|
49
|
+
"""Render a colormap to a PNG image.
|
|
50
|
+
|
|
51
|
+
Parameters
|
|
52
|
+
----------
|
|
53
|
+
cmap : matplotlib.colors.Colormap
|
|
54
|
+
Colormap to render.
|
|
55
|
+
file_name : str
|
|
56
|
+
Output PNG path.
|
|
57
|
+
orientation : str
|
|
58
|
+
``"horizontal"`` or ``"vertical"``.
|
|
59
|
+
vmin, vmax : float
|
|
60
|
+
Value range for the colour scale.
|
|
61
|
+
legend_label : str
|
|
62
|
+
Label shown alongside the colorbar.
|
|
63
|
+
"""
|
|
64
|
+
if orientation == "horizontal":
|
|
65
|
+
fig = plt.figure(figsize=(2.5, 1))
|
|
66
|
+
ax = fig.add_axes([0.05, 0.80, 0.9, 0.15])
|
|
67
|
+
else:
|
|
68
|
+
fig = plt.figure(figsize=(1, 2.5))
|
|
69
|
+
ax = fig.add_axes([0.80, 0.05, 0.15, 0.90])
|
|
70
|
+
|
|
71
|
+
norm = mpl.colors.Normalize(vmin=vmin, vmax=vmax)
|
|
72
|
+
cb = mpl.colorbar.ColorbarBase(
|
|
73
|
+
ax, cmap=cmap, norm=norm, orientation=orientation, label=legend_label
|
|
74
|
+
)
|
|
75
|
+
cb.ax.tick_params(labelsize=6)
|
|
76
|
+
|
|
77
|
+
fig.savefig(file_name, dpi=150, bbox_inches="tight")
|
|
78
|
+
plt.close(fig)
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def read_colormap(file_name: str) -> np.ndarray:
|
|
82
|
+
"""Read a colormap from a whitespace-separated RGB text file.
|
|
83
|
+
|
|
84
|
+
Parameters
|
|
85
|
+
----------
|
|
86
|
+
file_name : str
|
|
87
|
+
Path to the colormap file.
|
|
88
|
+
|
|
89
|
+
Returns
|
|
90
|
+
-------
|
|
91
|
+
np.ndarray
|
|
92
|
+
Array of shape ``(N, 3)`` with RGB values.
|
|
93
|
+
"""
|
|
94
|
+
df = pd.read_csv(
|
|
95
|
+
file_name,
|
|
96
|
+
index_col=False,
|
|
97
|
+
header=None,
|
|
98
|
+
sep=r"\s+",
|
|
99
|
+
names=["r", "g", "b"],
|
|
100
|
+
)
|
|
101
|
+
return df.to_numpy()
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def rgb2hex(rgb: Tuple[int, int, int]) -> str:
|
|
105
|
+
"""Convert an RGB tuple to a hex colour string.
|
|
106
|
+
|
|
107
|
+
Parameters
|
|
108
|
+
----------
|
|
109
|
+
rgb : Tuple[int, int, int]
|
|
110
|
+
Red, green, blue values (0-255).
|
|
111
|
+
|
|
112
|
+
Returns
|
|
113
|
+
-------
|
|
114
|
+
str
|
|
115
|
+
Six-character hex string (no leading ``#``).
|
|
116
|
+
"""
|
|
117
|
+
return "%02x%02x%02x" % rgb
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"""File I/O utilities for various Deltares and common data formats."""
|
|
2
|
+
|
|
3
|
+
from cht_utils.fileio.deltares_ini import IniStruct as IniStruct
|
|
4
|
+
from cht_utils.fileio.deltares_ini import Keyword as Keyword
|
|
5
|
+
from cht_utils.fileio.deltares_ini import Section as Section
|
|
6
|
+
from cht_utils.fileio.json_js import read_json_js as read_json_js
|
|
7
|
+
from cht_utils.fileio.json_js import write_csv_js as write_csv_js
|
|
8
|
+
from cht_utils.fileio.json_js import write_json_js as write_json_js
|
|
9
|
+
from cht_utils.fileio.pli_file import gdf2pli as gdf2pli
|
|
10
|
+
from cht_utils.fileio.pli_file import gdf2pol as gdf2pol
|
|
11
|
+
from cht_utils.fileio.pli_file import pli2gdf as pli2gdf
|
|
12
|
+
from cht_utils.fileio.pli_file import pol2gdf as pol2gdf
|
|
13
|
+
from cht_utils.fileio.pli_file import read_pli_file as read_pli_file
|
|
14
|
+
from cht_utils.fileio.tekal import tekal as tekal
|
|
15
|
+
from cht_utils.fileio.tekal import tekalblock as tekalblock
|
|
16
|
+
from cht_utils.fileio.xml import dict2xml as dict2xml
|
|
17
|
+
from cht_utils.fileio.xml import get_value as get_xml_value # noqa: F401
|
|
18
|
+
from cht_utils.fileio.xml import obj2xml as obj2xml
|
|
19
|
+
from cht_utils.fileio.xml import xml2obj as xml2obj
|
|
20
|
+
from cht_utils.fileio.yaml import dict2yaml as dict2yaml
|
|
21
|
+
from cht_utils.fileio.yaml import yaml2dict as yaml2dict
|