dfm-tools 0.42.0__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.
- dfm_tools/__init__.py +24 -0
- dfm_tools/bathymetry.py +101 -0
- dfm_tools/coastlines.py +186 -0
- dfm_tools/data.py +336 -0
- dfm_tools/deprecated_functions.py +72 -0
- dfm_tools/download.py +561 -0
- dfm_tools/errors.py +2 -0
- dfm_tools/get_nc.py +578 -0
- dfm_tools/get_nc_helpers.py +169 -0
- dfm_tools/hydrolib_helpers.py +700 -0
- dfm_tools/interpolate_grid2bnd.py +615 -0
- dfm_tools/linebuilder.py +92 -0
- dfm_tools/meshkernel_helpers.py +486 -0
- dfm_tools/modelbuilder.py +523 -0
- dfm_tools/modplot.py +700 -0
- dfm_tools/observations.py +1291 -0
- dfm_tools/settings.py +32 -0
- dfm_tools/xarray_helpers.py +432 -0
- dfm_tools/xugrid_helpers.py +633 -0
- dfm_tools-0.42.0.dist-info/METADATA +80 -0
- dfm_tools-0.42.0.dist-info/RECORD +24 -0
- dfm_tools-0.42.0.dist-info/WHEEL +5 -0
- dfm_tools-0.42.0.dist-info/licenses/LICENSE +674 -0
- dfm_tools-0.42.0.dist-info/top_level.txt +1 -0
dfm_tools/__init__.py
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Pre- and postprocessing D-FlowFM model input and output files
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
__author__ = """Jelmer Veenstra"""
|
|
6
|
+
__email__ = "jelmer.veenstra@deltares.nl"
|
|
7
|
+
__version__ = "0.42.0"
|
|
8
|
+
|
|
9
|
+
from dfm_tools.deprecated_functions import *
|
|
10
|
+
from dfm_tools.download import *
|
|
11
|
+
from dfm_tools.get_nc import *
|
|
12
|
+
from dfm_tools.get_nc_helpers import *
|
|
13
|
+
from dfm_tools.hydrolib_helpers import *
|
|
14
|
+
from dfm_tools.meshkernel_helpers import *
|
|
15
|
+
from dfm_tools.interpolate_grid2bnd import *
|
|
16
|
+
from dfm_tools.linebuilder import *
|
|
17
|
+
from dfm_tools.modplot import *
|
|
18
|
+
from dfm_tools.xarray_helpers import *
|
|
19
|
+
from dfm_tools.xugrid_helpers import *
|
|
20
|
+
from dfm_tools.bathymetry import *
|
|
21
|
+
from dfm_tools.coastlines import *
|
|
22
|
+
from dfm_tools import data
|
|
23
|
+
from dfm_tools.modelbuilder import *
|
|
24
|
+
from dfm_tools.observations import *
|
dfm_tools/bathymetry.py
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
import xarray as xr
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def write_bathy_toasc(filename_asc,lon_sel_ext,lat_sel_ext,elev_sel_ext,asc_fmt='%9.2f',nodata_val=32767):
|
|
6
|
+
"""
|
|
7
|
+
empty docstring
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
print('writing to asc file')
|
|
11
|
+
if elev_sel_ext.shape != (lat_sel_ext.shape[0], lon_sel_ext.shape[0]):
|
|
12
|
+
raise ValueError('resulting shape of elev_sel_ext %s is not consistent with lat_sel_ext/lon_sel_ext vars %s'%(elev_sel_ext.shape,(lat_sel_ext.shape[0], lon_sel_ext.shape[0])))
|
|
13
|
+
if isinstance(elev_sel_ext,np.ma.core.MaskedArray): #masked has to be filled in order for the nans to be visible
|
|
14
|
+
elev_sel_ext = elev_sel_ext.filled(np.nan)
|
|
15
|
+
if np.isnan(elev_sel_ext).sum()>0:
|
|
16
|
+
elev_sel_ext = elev_sel_ext.copy()
|
|
17
|
+
elev_sel_ext[np.isnan(elev_sel_ext)] = nodata_val
|
|
18
|
+
print('replaced nan values with %d'%(nodata_val))
|
|
19
|
+
resinv_lonlat = np.round(1/np.diff(lon_sel_ext[:2]),2)[0]
|
|
20
|
+
resinv_lat = np.round(1/np.diff(lat_sel_ext[:2]),2)[0]
|
|
21
|
+
if resinv_lonlat!=resinv_lat:
|
|
22
|
+
raise ValueError('inconsistent resolution over lat/lon')
|
|
23
|
+
|
|
24
|
+
with open(filename_asc,'w') as file_asc:
|
|
25
|
+
file_asc.write('ncols %d\n'%(elev_sel_ext.shape[1]))
|
|
26
|
+
file_asc.write('nrows %d\n'%(elev_sel_ext.shape[0]))
|
|
27
|
+
file_asc.write('xllcenter %13.8f\n'%(lon_sel_ext[0]))
|
|
28
|
+
file_asc.write('yllcenter %13.8f\n'%(lat_sel_ext[0]))
|
|
29
|
+
file_asc.write('cellsize %16.11f\n'%(1/resinv_lonlat))
|
|
30
|
+
#file_asc.write('NODATA_value %d\n'%(nodata_val))
|
|
31
|
+
file_asc.write('NODATA_value '+asc_fmt%(nodata_val)+'\n')
|
|
32
|
+
with open(filename_asc,'a') as file_asc:
|
|
33
|
+
np.savetxt(file_asc,np.flip(elev_sel_ext,axis=0),fmt=asc_fmt)
|
|
34
|
+
print('...finished')
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def read_asc(file_asc:str) -> xr.Dataset:
|
|
38
|
+
"""
|
|
39
|
+
Reading asc file into a xarray.Dataset
|
|
40
|
+
|
|
41
|
+
Parameters
|
|
42
|
+
----------
|
|
43
|
+
file_asc : str
|
|
44
|
+
asc file with header ncols, nrows, xllcenter, yllcenter, cellsize, NODATA_value.
|
|
45
|
+
|
|
46
|
+
Returns
|
|
47
|
+
-------
|
|
48
|
+
ds_asc : xr.Dataset
|
|
49
|
+
xarray.Dataset with the data from the asc file as an array and
|
|
50
|
+
the lat/lon coordinates as separate coordinate variables.
|
|
51
|
+
|
|
52
|
+
"""
|
|
53
|
+
# read header
|
|
54
|
+
header_dict = {}
|
|
55
|
+
with open(file_asc) as f:
|
|
56
|
+
for linenum, line in enumerate(f, 1):
|
|
57
|
+
linesplit = line.split()
|
|
58
|
+
linestart = linesplit[0]
|
|
59
|
+
linestop = linesplit[-1]
|
|
60
|
+
|
|
61
|
+
try:
|
|
62
|
+
# try to convert it to float, this will fail for headerlines
|
|
63
|
+
# it will succeed for numeric lines, but then the loop breaks
|
|
64
|
+
float(linestart)
|
|
65
|
+
break
|
|
66
|
+
except ValueError:
|
|
67
|
+
# convert header values to int if possible or float if not
|
|
68
|
+
try:
|
|
69
|
+
header_value = int(linestop)
|
|
70
|
+
except ValueError:
|
|
71
|
+
header_value = float(linestop)
|
|
72
|
+
header_dict[linestart] = header_value
|
|
73
|
+
skiprows = linenum
|
|
74
|
+
|
|
75
|
+
# read data
|
|
76
|
+
asc_np = np.loadtxt(file_asc, skiprows=skiprows, dtype=float)
|
|
77
|
+
|
|
78
|
+
# derive x/y values and assert shape
|
|
79
|
+
num_x = header_dict['ncols']
|
|
80
|
+
num_y = header_dict['nrows']
|
|
81
|
+
start_x = header_dict['xllcenter']
|
|
82
|
+
start_y = header_dict['yllcenter']
|
|
83
|
+
step = header_dict['cellsize']
|
|
84
|
+
nodata = header_dict['NODATA_value']
|
|
85
|
+
|
|
86
|
+
assert asc_np.shape == (num_y, num_x)
|
|
87
|
+
|
|
88
|
+
x_vals = np.arange(0, num_x) * step + start_x
|
|
89
|
+
y_vals = np.arange(0, num_y) * step + start_y
|
|
90
|
+
|
|
91
|
+
# flip over latitude and replace nodata with nan
|
|
92
|
+
asc_np = np.flipud(asc_np)
|
|
93
|
+
asc_np[asc_np==nodata] = np.nan
|
|
94
|
+
|
|
95
|
+
ds_asc = xr.Dataset()
|
|
96
|
+
ds_asc['lon'] = xr.DataArray(x_vals, dims=('lon'))
|
|
97
|
+
ds_asc['lat'] = xr.DataArray(y_vals, dims=('lat'))
|
|
98
|
+
ds_asc['data'] = xr.DataArray(asc_np, dims=('lat','lon'))
|
|
99
|
+
ds_asc = ds_asc.assign_attrs(header_dict)
|
|
100
|
+
return ds_asc
|
|
101
|
+
|
dfm_tools/coastlines.py
ADDED
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import geopandas as gpd
|
|
3
|
+
import matplotlib.pyplot as plt
|
|
4
|
+
import pandas as pd
|
|
5
|
+
import datetime as dt
|
|
6
|
+
from dfm_tools.data import gshhs_coastlines_shp
|
|
7
|
+
|
|
8
|
+
__all__ = ["get_coastlines_gdb",
|
|
9
|
+
"plot_coastlines",
|
|
10
|
+
"get_borders_gdb",
|
|
11
|
+
"plot_borders",
|
|
12
|
+
]
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def bbox_convert_crs(bbox, crs):
|
|
16
|
+
"""
|
|
17
|
+
convert bbox from input crs to WGS84
|
|
18
|
+
"""
|
|
19
|
+
bbox_points = gpd.points_from_xy(x=[bbox[0],bbox[2]], y=[bbox[1],bbox[3]], crs=crs)
|
|
20
|
+
bbox_points = bbox_points.to_crs('EPSG:4326') #convert to WGS84
|
|
21
|
+
bbox = (bbox_points.x[0], bbox_points.y[0], bbox_points.x[1], bbox_points.y[1])
|
|
22
|
+
return bbox
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def get_coastlines_gdb(res:str='h', bbox:tuple = (-180, -90, 180, 90), min_area:float = 0, crs:str = None, columns:list = ['area']) -> gpd.geoseries.GeoSeries:
|
|
26
|
+
"""
|
|
27
|
+
GSHHS coastlines: https://www.ngdc.noaa.gov/mgg/shorelines/data/gshhg/latest/readme.txt
|
|
28
|
+
geopandas docs https://geopandas.org/en/stable/docs/reference/api/geopandas.read_file.html
|
|
29
|
+
|
|
30
|
+
Parameters
|
|
31
|
+
----------
|
|
32
|
+
res : str, optional
|
|
33
|
+
f(ull), h(igh), i(ntermediate), l(ow), c(oarse) resolution. The default is 'h'.
|
|
34
|
+
bbox : tuple, optional
|
|
35
|
+
(minx, miny, maxx, maxy), also includes shapes that are partly in the bbox. The default is (-180, -90, 180, 90).
|
|
36
|
+
min_area : float, optional
|
|
37
|
+
in km2, min_area>0 speeds up process. The default is 0.
|
|
38
|
+
crs : str, optional
|
|
39
|
+
coordinate reference system
|
|
40
|
+
columns : list, optional
|
|
41
|
+
which shapefile columns to include, None gives all. The default is ['area'].
|
|
42
|
+
|
|
43
|
+
Returns
|
|
44
|
+
-------
|
|
45
|
+
coastlines_gdb : TYPE
|
|
46
|
+
DESCRIPTION.
|
|
47
|
+
|
|
48
|
+
"""
|
|
49
|
+
|
|
50
|
+
if res not in 'fhilc':
|
|
51
|
+
raise KeyError(f'invalid res="{res}", resolution options are f(ull), h(igh), i(ntermediate), l(ow), c(oarse)')
|
|
52
|
+
if crs is not None:
|
|
53
|
+
bbox = bbox_convert_crs(bbox, crs)
|
|
54
|
+
|
|
55
|
+
# download gshhs data if not present and return dir
|
|
56
|
+
dir_gshhs = gshhs_coastlines_shp()
|
|
57
|
+
|
|
58
|
+
file_shp_L1 = os.path.join(dir_gshhs,'GSHHS_shp',res,f'GSHHS_{res}_L1.shp') #coastlines
|
|
59
|
+
file_shp_L6 = os.path.join(dir_gshhs,'GSHHS_shp',res,f'GSHHS_{res}_L6.shp') #Antarctic grounding-line polygons
|
|
60
|
+
file_shp_L2 = os.path.join(dir_gshhs,'GSHHS_shp',res,f'GSHHS_{res}_L2.shp') #lakes
|
|
61
|
+
file_shp_L3 = os.path.join(dir_gshhs,'GSHHS_shp',res,f'GSHHS_{res}_L3.shp') #islands-in-lakes
|
|
62
|
+
|
|
63
|
+
print('>> reading coastlines: ',end='')
|
|
64
|
+
dtstart = dt.datetime.now()
|
|
65
|
+
coastlines_gdb_L1 = gpd.read_file(file_shp_L1, columns=columns, where=f"area>{min_area}", bbox=bbox)
|
|
66
|
+
coastlines_gdb_L6 = gpd.read_file(file_shp_L6, columns=columns, where=f"area>{min_area}", bbox=bbox)
|
|
67
|
+
coastlines_gdb_list = [coastlines_gdb_L1,coastlines_gdb_L6]
|
|
68
|
+
if len(coastlines_gdb_L1)<2: #if only one L1 polygon is selected, automatically add lakes and islands-in-lakes
|
|
69
|
+
coastlines_gdb_L2 = gpd.read_file(file_shp_L2, columns=columns, where=f"area>{min_area}", bbox=bbox)
|
|
70
|
+
coastlines_gdb_list.append(coastlines_gdb_L2)
|
|
71
|
+
coastlines_gdb_L3 = gpd.read_file(file_shp_L3, columns=columns, where=f"area>{min_area}", bbox=bbox)
|
|
72
|
+
coastlines_gdb_list.append(coastlines_gdb_L3)
|
|
73
|
+
|
|
74
|
+
# remove empty geodataframes from list to avoid FutureWarning
|
|
75
|
+
# escape for empty resulting list and concatenate otherwise
|
|
76
|
+
coastlines_gdb_list = [x for x in coastlines_gdb_list if not x.empty]
|
|
77
|
+
if not coastlines_gdb_list:
|
|
78
|
+
return gpd.GeoDataFrame()
|
|
79
|
+
coastlines_gdb = pd.concat(coastlines_gdb_list)
|
|
80
|
+
print(f'{(dt.datetime.now()-dtstart).total_seconds():.2f} sec')
|
|
81
|
+
|
|
82
|
+
if crs:
|
|
83
|
+
coastlines_gdb = coastlines_gdb.to_crs(crs)
|
|
84
|
+
|
|
85
|
+
return coastlines_gdb
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def get_borders_gdb(res:str='h', bbox:tuple = (-180, -90, 180, 90), crs:str = None) -> gpd.geoseries.GeoSeries:
|
|
89
|
+
"""
|
|
90
|
+
GSHHS coastlines: https://www.ngdc.noaa.gov/mgg/shorelines/data/gshhg/latest/readme.txt
|
|
91
|
+
geopandas docs https://geopandas.org/en/stable/docs/reference/api/geopandas.read_file.html
|
|
92
|
+
|
|
93
|
+
Parameters
|
|
94
|
+
----------
|
|
95
|
+
res : str, optional
|
|
96
|
+
f(ull), h(igh), i(ntermediate), l(ow), c(oarse) resolution. The default is 'h'.
|
|
97
|
+
bbox : tuple, optional
|
|
98
|
+
(minx, miny, maxx, maxy), also includes shapes that are partly in the bbox. The default is (-180, -90, 180, 90).
|
|
99
|
+
crs : str, optional
|
|
100
|
+
coordinate reference system
|
|
101
|
+
|
|
102
|
+
Returns
|
|
103
|
+
-------
|
|
104
|
+
coastlines_gdb : TYPE
|
|
105
|
+
DESCRIPTION.
|
|
106
|
+
|
|
107
|
+
"""
|
|
108
|
+
|
|
109
|
+
if res not in 'fhilc':
|
|
110
|
+
raise KeyError(f'invalid res="{res}", resolution options are f(ull), h(igh), i(ntermediate), l(ow), c(oarse)')
|
|
111
|
+
if crs is not None:
|
|
112
|
+
bbox = bbox_convert_crs(bbox, crs)
|
|
113
|
+
|
|
114
|
+
# download gshhs data if not present and return dir
|
|
115
|
+
dir_gshhs = gshhs_coastlines_shp()
|
|
116
|
+
|
|
117
|
+
file_shp_L1 = os.path.join(dir_gshhs,'WDBII_shp',res,f'WDBII_border_{res}_L1.shp') #borders
|
|
118
|
+
|
|
119
|
+
print('>> reading country borders: ',end='')
|
|
120
|
+
dtstart = dt.datetime.now()
|
|
121
|
+
coastlines_gdb = gpd.read_file(file_shp_L1, bbox=bbox)
|
|
122
|
+
print(f'{(dt.datetime.now()-dtstart).total_seconds():.2f} sec')
|
|
123
|
+
|
|
124
|
+
if crs:
|
|
125
|
+
coastlines_gdb = coastlines_gdb.to_crs(crs)
|
|
126
|
+
|
|
127
|
+
return coastlines_gdb
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def plot_coastlines(ax=None, res:str='h', min_area:float = 0, crs=None, **kwargs):
|
|
131
|
+
"""
|
|
132
|
+
get coastlines with get_coastlines_gdb and bbox depending on axlims, plot on ax and set axlims back to original values
|
|
133
|
+
"""
|
|
134
|
+
#TODO: if ax is GeoAxis, get crs from ax
|
|
135
|
+
|
|
136
|
+
if ax is None:
|
|
137
|
+
ax = plt.gca()
|
|
138
|
+
|
|
139
|
+
xlim = ax.get_xlim()
|
|
140
|
+
ylim = ax.get_ylim()
|
|
141
|
+
bbox = (xlim[0], ylim[0], xlim[1], ylim[1])
|
|
142
|
+
|
|
143
|
+
if 'edgecolor' not in kwargs:
|
|
144
|
+
kwargs['edgecolor'] = 'k'
|
|
145
|
+
if 'facecolor' not in kwargs:
|
|
146
|
+
kwargs['facecolor'] = 'none'
|
|
147
|
+
if 'linewidth' not in kwargs:
|
|
148
|
+
kwargs['linewidth'] = 0.5
|
|
149
|
+
|
|
150
|
+
coastlines_gdb = get_coastlines_gdb(bbox=bbox, res=res, min_area=min_area, crs=crs)
|
|
151
|
+
if coastlines_gdb.empty:
|
|
152
|
+
return
|
|
153
|
+
|
|
154
|
+
coastlines_gdb.plot(ax=ax, **kwargs)
|
|
155
|
+
|
|
156
|
+
ax.set_xlim(xlim)
|
|
157
|
+
ax.set_ylim(ylim)
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
def plot_borders(ax=None, res:str='h', crs=None, **kwargs):
|
|
161
|
+
"""
|
|
162
|
+
get borders with get_borders_gdb and bbox depending on axlims, plot on ax and set axlims back to original values
|
|
163
|
+
"""
|
|
164
|
+
#TODO: if ax is GeoAxis, get crs from ax
|
|
165
|
+
|
|
166
|
+
if ax is None:
|
|
167
|
+
ax = plt.gca()
|
|
168
|
+
|
|
169
|
+
xlim = ax.get_xlim()
|
|
170
|
+
ylim = ax.get_ylim()
|
|
171
|
+
bbox = (xlim[0], ylim[0], xlim[1], ylim[1])
|
|
172
|
+
|
|
173
|
+
if 'edgecolor' not in kwargs:
|
|
174
|
+
kwargs['edgecolor'] = 'grey'
|
|
175
|
+
if 'linewidth' not in kwargs:
|
|
176
|
+
kwargs['linewidth'] = 0.5
|
|
177
|
+
|
|
178
|
+
coastlines_gdb = get_borders_gdb(bbox=bbox, res=res, crs=crs)
|
|
179
|
+
if coastlines_gdb.empty:
|
|
180
|
+
return
|
|
181
|
+
|
|
182
|
+
coastlines_gdb.plot(ax=ax, **kwargs)
|
|
183
|
+
|
|
184
|
+
ax.set_xlim(xlim)
|
|
185
|
+
ax.set_ylim(ylim)
|
|
186
|
+
|
dfm_tools/data.py
ADDED
|
@@ -0,0 +1,336 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import requests
|
|
3
|
+
import xarray as xr
|
|
4
|
+
import xugrid as xu
|
|
5
|
+
import pooch
|
|
6
|
+
import zipfile
|
|
7
|
+
from dfm_tools.xugrid_helpers import (open_partitioned_dataset,
|
|
8
|
+
open_dataset_delft3d4,
|
|
9
|
+
enrich_rst_with_map,
|
|
10
|
+
)
|
|
11
|
+
from dfm_tools.xarray_helpers import preprocess_hisnc
|
|
12
|
+
|
|
13
|
+
__all__ = ["fm_grevelingen_map",
|
|
14
|
+
"fm_grevelingen_his",
|
|
15
|
+
"fm_grevelingen_net",
|
|
16
|
+
"fm_curvedbend_map",
|
|
17
|
+
"fm_curvedbend_his",
|
|
18
|
+
"fm_westernscheldt_map",
|
|
19
|
+
"d3d_westernscheldt_trim",
|
|
20
|
+
"d3d_curvedbend_trim",
|
|
21
|
+
"d3d_curvedbend_trih",
|
|
22
|
+
]
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def get_dir_testdata():
|
|
26
|
+
# create cache dir like %USERPROFILE%/AppData/Local/dfm_tools/dfm_tools/Cache
|
|
27
|
+
# TODO: add SHA256 checking and more, like: https://github.com/Deltares/xugrid/blob/main/xugrid/data/sample_data.py
|
|
28
|
+
dir_testdata = str(pooch.os_cache('dfm_tools'))
|
|
29
|
+
os.makedirs(dir_testdata, exist_ok=True)
|
|
30
|
+
return dir_testdata
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def maybe_download_opendap_data(file_nc,dir_subfolder=None):
|
|
34
|
+
if os.path.exists(file_nc): # only download if file does not exist already
|
|
35
|
+
return
|
|
36
|
+
|
|
37
|
+
#opendap catalog: https://opendap.deltares.nl/thredds/catalog/opendap/deltares/Delft3D/netcdf_example_files/catalog.html
|
|
38
|
+
opendap_url = 'https://opendap.deltares.nl/thredds/fileServer/opendap/deltares/Delft3D/netcdf_example_files'
|
|
39
|
+
if dir_subfolder is not None:
|
|
40
|
+
opendap_url = f'{opendap_url}/{dir_subfolder}'
|
|
41
|
+
fname = os.path.basename(file_nc)
|
|
42
|
+
file_url = f'{opendap_url}/{fname}'
|
|
43
|
+
|
|
44
|
+
print(f'downloading "{fname}" from opendap.deltares.nl to cachedir')
|
|
45
|
+
r = requests.get(file_url, allow_redirects=True)
|
|
46
|
+
r.raise_for_status() #raise HTTPError if url not exists
|
|
47
|
+
with open(file_nc, 'wb') as f:
|
|
48
|
+
f.write(r.content)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def fm_grevelingen_map(return_filepath:bool = False) -> xu.UgridDataset:
|
|
52
|
+
|
|
53
|
+
dir_subfolder = 'DFM_grevelingen_3D'
|
|
54
|
+
dir_testdata = get_dir_testdata()
|
|
55
|
+
|
|
56
|
+
file_nc_pat = os.path.join(dir_testdata,'Grevelingen-FM_0*_map.nc')
|
|
57
|
+
|
|
58
|
+
#download data if not present
|
|
59
|
+
for part in range(8):
|
|
60
|
+
file_nc = file_nc_pat.replace('0*',f'{part:04d}')
|
|
61
|
+
maybe_download_opendap_data(file_nc,dir_subfolder)
|
|
62
|
+
|
|
63
|
+
#potentially only return filepath of downloaded file(s)
|
|
64
|
+
filepath = file_nc_pat
|
|
65
|
+
if return_filepath:
|
|
66
|
+
return filepath
|
|
67
|
+
|
|
68
|
+
#open as xugrid.UgridDataset
|
|
69
|
+
uds = open_partitioned_dataset(filepath)
|
|
70
|
+
return uds
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def fm_grevelingen_his(return_filepath:bool = False) -> xr.Dataset:
|
|
74
|
+
|
|
75
|
+
dir_subfolder = 'DFM_grevelingen_3D'
|
|
76
|
+
dir_testdata = get_dir_testdata()
|
|
77
|
+
|
|
78
|
+
#download data if not present
|
|
79
|
+
file_nc = os.path.join(dir_testdata,'Grevelingen-FM_0000_his.nc')
|
|
80
|
+
maybe_download_opendap_data(file_nc,dir_subfolder)
|
|
81
|
+
|
|
82
|
+
#potentially only return filepath of downloaded file(s)
|
|
83
|
+
filepath = file_nc
|
|
84
|
+
if return_filepath:
|
|
85
|
+
return filepath
|
|
86
|
+
|
|
87
|
+
#open as xarray.Dataset
|
|
88
|
+
ds = xr.open_mfdataset(filepath,preprocess=preprocess_hisnc)
|
|
89
|
+
return ds
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def fm_grevelingen_net(return_filepath:bool = False) -> xu.UgridDataset:
|
|
93
|
+
|
|
94
|
+
dir_subfolder = 'DFM_grevelingen_3D'
|
|
95
|
+
dir_testdata = get_dir_testdata()
|
|
96
|
+
|
|
97
|
+
#download data if not present
|
|
98
|
+
file_nc = os.path.join(dir_testdata,'Grevelingen_FM_grid_20190603_net.nc')
|
|
99
|
+
maybe_download_opendap_data(file_nc,dir_subfolder)
|
|
100
|
+
|
|
101
|
+
#potentially only return filepath of downloaded file(s)
|
|
102
|
+
filepath = file_nc
|
|
103
|
+
if return_filepath:
|
|
104
|
+
return filepath
|
|
105
|
+
|
|
106
|
+
#open as xugrid.UgridDataset
|
|
107
|
+
uds = open_partitioned_dataset(filepath)
|
|
108
|
+
return uds
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def fm_curvedbend_map(return_filepath:bool = False) -> xu.UgridDataset:
|
|
112
|
+
|
|
113
|
+
dir_subfolder = 'DFM_curvedbend_3D'
|
|
114
|
+
dir_testdata = get_dir_testdata()
|
|
115
|
+
|
|
116
|
+
#download data if not present
|
|
117
|
+
file_nc = os.path.join(dir_testdata,'cb_3d_map.nc')
|
|
118
|
+
maybe_download_opendap_data(file_nc,dir_subfolder)
|
|
119
|
+
|
|
120
|
+
#potentially only return filepath of downloaded file(s)
|
|
121
|
+
filepath = file_nc
|
|
122
|
+
if return_filepath:
|
|
123
|
+
return filepath
|
|
124
|
+
|
|
125
|
+
#open as xugrid.UgridDataset
|
|
126
|
+
uds = open_partitioned_dataset(filepath)
|
|
127
|
+
return uds
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def fm_curvedbend_his(return_filepath:bool = False) -> xr.Dataset:
|
|
131
|
+
|
|
132
|
+
dir_subfolder = 'DFM_curvedbend_3D'
|
|
133
|
+
dir_testdata = get_dir_testdata()
|
|
134
|
+
|
|
135
|
+
#download data if not present
|
|
136
|
+
file_nc = os.path.join(dir_testdata,'cb_3d_his.nc')
|
|
137
|
+
maybe_download_opendap_data(file_nc,dir_subfolder)
|
|
138
|
+
|
|
139
|
+
#potentially only return filepath of downloaded file(s)
|
|
140
|
+
filepath = file_nc
|
|
141
|
+
if return_filepath:
|
|
142
|
+
return filepath
|
|
143
|
+
|
|
144
|
+
#open as xarray.Dataset
|
|
145
|
+
ds = xr.open_mfdataset(filepath,preprocess=preprocess_hisnc)
|
|
146
|
+
return ds
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
def fm_westernscheldt_map(return_filepath:bool = False) -> xu.UgridDataset:
|
|
150
|
+
# from p:\dflowfm\maintenance\JIRA\05000-05999\05477\c103_ws_3d_fourier
|
|
151
|
+
|
|
152
|
+
dir_subfolder = 'DFM_westernscheldt_3D'
|
|
153
|
+
dir_testdata = get_dir_testdata()
|
|
154
|
+
|
|
155
|
+
#download data if not present
|
|
156
|
+
file_nc = os.path.join(dir_testdata,'westerscheldt01_0subst_map.nc')
|
|
157
|
+
maybe_download_opendap_data(file_nc,dir_subfolder)
|
|
158
|
+
|
|
159
|
+
#potentially only return filepath of downloaded file(s)
|
|
160
|
+
filepath = file_nc
|
|
161
|
+
if return_filepath:
|
|
162
|
+
return filepath
|
|
163
|
+
|
|
164
|
+
#open as UgridDataset
|
|
165
|
+
uds = open_partitioned_dataset(filepath, remove_edges=True)
|
|
166
|
+
return uds
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
def fm_westernscheldt_fou(return_filepath:bool = False) -> xu.UgridDataset:
|
|
170
|
+
# from p:\dflowfm\maintenance\JIRA\05000-05999\05477\c103_ws_3d_fourier
|
|
171
|
+
|
|
172
|
+
dir_subfolder = 'DFM_westernscheldt_3D'
|
|
173
|
+
dir_testdata = get_dir_testdata()
|
|
174
|
+
|
|
175
|
+
#download data if not present
|
|
176
|
+
file_nc = os.path.join(dir_testdata,'westerscheldt01_0subst_fou.nc')
|
|
177
|
+
maybe_download_opendap_data(file_nc,dir_subfolder)
|
|
178
|
+
|
|
179
|
+
#potentially only return filepath of downloaded file(s)
|
|
180
|
+
filepath = file_nc
|
|
181
|
+
if return_filepath:
|
|
182
|
+
return filepath
|
|
183
|
+
|
|
184
|
+
#open as UgridDataset
|
|
185
|
+
uds = open_partitioned_dataset(filepath, remove_edges=True)
|
|
186
|
+
return uds
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
def fm_westernscheldt_rst(return_filepath:bool = False) -> xu.UgridDataset:
|
|
190
|
+
# from p:\dflowfm\maintenance\JIRA\05000-05999\05477\c103_ws_3d_fourier
|
|
191
|
+
|
|
192
|
+
dir_subfolder = 'DFM_westernscheldt_3D'
|
|
193
|
+
dir_testdata = get_dir_testdata()
|
|
194
|
+
|
|
195
|
+
#download data if not present
|
|
196
|
+
file_nc = os.path.join(dir_testdata,'westerscheldt01_0subst_20140101_004640_rst.nc')
|
|
197
|
+
maybe_download_opendap_data(file_nc,dir_subfolder)
|
|
198
|
+
|
|
199
|
+
#potentially only return filepath of downloaded file(s)
|
|
200
|
+
filepath = file_nc
|
|
201
|
+
if return_filepath:
|
|
202
|
+
return filepath
|
|
203
|
+
|
|
204
|
+
#open as UgridDataset
|
|
205
|
+
uds = open_partitioned_dataset(filepath, preprocess=enrich_rst_with_map)
|
|
206
|
+
return uds
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
def fm_westernscheldt_his(return_filepath:bool = False) -> xr.Dataset:
|
|
210
|
+
# from p:\dflowfm\maintenance\JIRA\05000-05999\05477\c103_ws_3d_fourier
|
|
211
|
+
|
|
212
|
+
dir_subfolder = 'DFM_westernscheldt_3D'
|
|
213
|
+
dir_testdata = get_dir_testdata()
|
|
214
|
+
|
|
215
|
+
#download data if not present
|
|
216
|
+
file_nc = os.path.join(dir_testdata,'westerscheldt01_0subst_his.nc')
|
|
217
|
+
maybe_download_opendap_data(file_nc,dir_subfolder)
|
|
218
|
+
|
|
219
|
+
#potentially only return filepath of downloaded file(s)
|
|
220
|
+
filepath = file_nc
|
|
221
|
+
if return_filepath:
|
|
222
|
+
return filepath
|
|
223
|
+
|
|
224
|
+
#open as xarray.Dataset
|
|
225
|
+
ds = xr.open_mfdataset(filepath,preprocess=preprocess_hisnc)
|
|
226
|
+
return ds
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
def d3d_westernscheldt_trim(return_filepath:bool = False) -> xu.UgridDataset:
|
|
230
|
+
|
|
231
|
+
dir_testdata = get_dir_testdata()
|
|
232
|
+
|
|
233
|
+
#download data if not present
|
|
234
|
+
file_nc = os.path.join(dir_testdata,'trim-westernscheldt_sph.nc')
|
|
235
|
+
maybe_download_opendap_data(file_nc)
|
|
236
|
+
|
|
237
|
+
#potentially only return filepath of downloaded file(s)
|
|
238
|
+
filepath = file_nc
|
|
239
|
+
if return_filepath:
|
|
240
|
+
return filepath
|
|
241
|
+
|
|
242
|
+
#open as UgridDataset
|
|
243
|
+
uds = open_dataset_delft3d4(filepath)
|
|
244
|
+
return uds
|
|
245
|
+
|
|
246
|
+
|
|
247
|
+
def d3d_curvedbend_trim(return_filepath:bool = False) -> xu.UgridDataset:
|
|
248
|
+
|
|
249
|
+
dir_testdata = get_dir_testdata()
|
|
250
|
+
|
|
251
|
+
#download data if not present
|
|
252
|
+
file_nc = os.path.join(dir_testdata,'trim-cb2-sal-added-3d.nc')
|
|
253
|
+
maybe_download_opendap_data(file_nc)
|
|
254
|
+
|
|
255
|
+
#potentially only return filepath of downloaded file(s)
|
|
256
|
+
filepath = file_nc
|
|
257
|
+
if return_filepath:
|
|
258
|
+
return filepath
|
|
259
|
+
|
|
260
|
+
#open as UgridDataset
|
|
261
|
+
uds = open_dataset_delft3d4(filepath)
|
|
262
|
+
return uds
|
|
263
|
+
|
|
264
|
+
|
|
265
|
+
def d3d_curvedbend_trih(return_filepath:bool = False) -> xr.Dataset:
|
|
266
|
+
|
|
267
|
+
dir_testdata = get_dir_testdata()
|
|
268
|
+
|
|
269
|
+
#download data if not present
|
|
270
|
+
file_nc = os.path.join(dir_testdata,'trih-cb2-sal-added-3d.nc')
|
|
271
|
+
maybe_download_opendap_data(file_nc)
|
|
272
|
+
|
|
273
|
+
#potentially only return filepath of downloaded file(s)
|
|
274
|
+
filepath = file_nc
|
|
275
|
+
if return_filepath:
|
|
276
|
+
return filepath
|
|
277
|
+
|
|
278
|
+
#open as UgridDataset
|
|
279
|
+
ds = xr.open_mfdataset(filepath,preprocess=preprocess_hisnc)
|
|
280
|
+
return ds
|
|
281
|
+
|
|
282
|
+
|
|
283
|
+
def gshhs_coastlines_shp() -> str:
|
|
284
|
+
"""
|
|
285
|
+
Downloads and unzips GSHHS coastlines from NOAA if not present.
|
|
286
|
+
Checks presence of files for all five resolutions.
|
|
287
|
+
|
|
288
|
+
Returns
|
|
289
|
+
-------
|
|
290
|
+
str
|
|
291
|
+
DESCRIPTION.
|
|
292
|
+
|
|
293
|
+
"""
|
|
294
|
+
|
|
295
|
+
dir_testdata = get_dir_testdata()
|
|
296
|
+
|
|
297
|
+
fname = 'gshhg-shp-2.3.7'
|
|
298
|
+
filepath_zip = os.path.join(dir_testdata, f"{fname}.zip")
|
|
299
|
+
dir_gshhs = os.path.join(dir_testdata, fname)
|
|
300
|
+
|
|
301
|
+
def download_gshhs(url):
|
|
302
|
+
url_base = url.split("/")[2]
|
|
303
|
+
fname_zip = url.split('/')[-1]
|
|
304
|
+
print(f'downloading "{fname_zip}" from {url_base} to cachedir')
|
|
305
|
+
# url is redirected to https://objects.githubusercontent.com
|
|
306
|
+
resp = requests.get(url, allow_redirects=True)
|
|
307
|
+
# raise HTTPError if url not exists
|
|
308
|
+
resp.raise_for_status()
|
|
309
|
+
return resp
|
|
310
|
+
|
|
311
|
+
# download zipfile if not present
|
|
312
|
+
if not os.path.exists(filepath_zip) and not os.path.exists(dir_gshhs):
|
|
313
|
+
file_url = f"https://github.com/GenericMappingTools/gshhg-gmt/releases/download/2.3.7/{fname}.zip"
|
|
314
|
+
resp = download_gshhs(file_url)
|
|
315
|
+
with open(filepath_zip, 'wb') as f:
|
|
316
|
+
f.write(resp.content)
|
|
317
|
+
|
|
318
|
+
# unzip zipfile if unzipped folder not present
|
|
319
|
+
if not os.path.exists(dir_gshhs):
|
|
320
|
+
print(f'unzipping "{fname}.zip"')
|
|
321
|
+
with zipfile.ZipFile(filepath_zip, 'r') as zip_ref:
|
|
322
|
+
zip_ref.extractall(dir_gshhs)
|
|
323
|
+
|
|
324
|
+
# construct filepath list and check existence of shapefiles
|
|
325
|
+
filepath_shp_list = [os.path.join(dir_gshhs,'GSHHS_shp',res,f'GSHHS_{res}_L1.shp') for res in ['f','h','i','l','c']]
|
|
326
|
+
for filepath_shp in filepath_shp_list:
|
|
327
|
+
assert os.path.exists(filepath_shp) #coastlines
|
|
328
|
+
assert os.path.exists(filepath_shp.replace('L1.shp','L2.shp')) #lakes
|
|
329
|
+
assert os.path.exists(filepath_shp.replace('L1.shp','L3.shp')) #islands-in-lakes
|
|
330
|
+
assert os.path.exists(filepath_shp.replace('L1.shp','L6.shp')) #Antarctic grounding-line polygons
|
|
331
|
+
filepath_shp_list = [os.path.join(dir_gshhs,'WDBII_shp',res,f'WDBII_border_{res}_L1.shp') for res in ['f','h','i','l','c']]
|
|
332
|
+
for filepath_shp in filepath_shp_list:
|
|
333
|
+
assert os.path.exists(filepath_shp) #coastlines
|
|
334
|
+
|
|
335
|
+
return dir_gshhs
|
|
336
|
+
|