hydamo-validation 1.3.0b1__tar.gz → 1.3.0b3__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.
Potentially problematic release.
This version of hydamo-validation might be problematic. Click here for more details.
- {hydamo_validation-1.3.0b1 → hydamo_validation-1.3.0b3}/PKG-INFO +1 -1
- hydamo_validation-1.3.0b3/hydamo_validation/__init__.py +22 -0
- hydamo_validation-1.3.0b3/hydamo_validation/functions/__init__.py +0 -0
- hydamo_validation-1.3.0b3/hydamo_validation/functions/general.py +319 -0
- hydamo_validation-1.3.0b3/hydamo_validation/functions/logic.py +338 -0
- hydamo_validation-1.3.0b3/hydamo_validation/functions/topologic.py +665 -0
- hydamo_validation-1.3.0b3/hydamo_validation/schemas/hydamo/HyDAMO_2.2.json +5946 -0
- hydamo_validation-1.3.0b3/hydamo_validation/schemas/hydamo/HyDAMO_2.3.json +6602 -0
- hydamo_validation-1.3.0b3/hydamo_validation/schemas/rules/rules_1.0.json +1134 -0
- hydamo_validation-1.3.0b3/hydamo_validation/schemas/rules/rules_1.1.json +1134 -0
- hydamo_validation-1.3.0b3/hydamo_validation/schemas/rules/rules_1.2.json +1128 -0
- hydamo_validation-1.3.0b3/hydamo_validation/schemas/rules/rules_1.3.json +1128 -0
- hydamo_validation-1.3.0b3/hydamo_validation/styles/hydroobject.qml +497 -0
- hydamo_validation-1.3.0b3/hydamo_validation/styles/hydroobject.sld +15 -0
- {hydamo_validation-1.3.0b1 → hydamo_validation-1.3.0b3}/hydamo_validation.egg-info/PKG-INFO +1 -1
- {hydamo_validation-1.3.0b1 → hydamo_validation-1.3.0b3}/hydamo_validation.egg-info/SOURCES.txt +12 -0
- {hydamo_validation-1.3.0b1 → hydamo_validation-1.3.0b3}/pyproject.toml +4 -1
- hydamo_validation-1.3.0b1/hydamo_validation/__init__.py +0 -13
- {hydamo_validation-1.3.0b1 → hydamo_validation-1.3.0b3}/LICENSE +0 -0
- {hydamo_validation-1.3.0b1 → hydamo_validation-1.3.0b3}/README.md +0 -0
- {hydamo_validation-1.3.0b1 → hydamo_validation-1.3.0b3}/hydamo_validation/datamodel.py +0 -0
- {hydamo_validation-1.3.0b1 → hydamo_validation-1.3.0b3}/hydamo_validation/datasets.py +0 -0
- {hydamo_validation-1.3.0b1 → hydamo_validation-1.3.0b3}/hydamo_validation/geometry.py +0 -0
- {hydamo_validation-1.3.0b1 → hydamo_validation-1.3.0b3}/hydamo_validation/logical_validation.py +0 -0
- {hydamo_validation-1.3.0b1 → hydamo_validation-1.3.0b3}/hydamo_validation/styles.py +0 -0
- {hydamo_validation-1.3.0b1 → hydamo_validation-1.3.0b3}/hydamo_validation/summaries.py +0 -0
- {hydamo_validation-1.3.0b1 → hydamo_validation-1.3.0b3}/hydamo_validation/syntax_validation.py +0 -0
- {hydamo_validation-1.3.0b1 → hydamo_validation-1.3.0b3}/hydamo_validation/utils.py +0 -0
- {hydamo_validation-1.3.0b1 → hydamo_validation-1.3.0b3}/hydamo_validation/validator.py +0 -0
- {hydamo_validation-1.3.0b1 → hydamo_validation-1.3.0b3}/hydamo_validation.egg-info/dependency_links.txt +0 -0
- {hydamo_validation-1.3.0b1 → hydamo_validation-1.3.0b3}/hydamo_validation.egg-info/requires.txt +0 -0
- {hydamo_validation-1.3.0b1 → hydamo_validation-1.3.0b3}/hydamo_validation.egg-info/top_level.txt +0 -0
- {hydamo_validation-1.3.0b1 → hydamo_validation-1.3.0b3}/hydamo_validation.egg-info/zip-safe +0 -0
- {hydamo_validation-1.3.0b1 → hydamo_validation-1.3.0b3}/setup.cfg +0 -0
- {hydamo_validation-1.3.0b1 → hydamo_validation-1.3.0b3}/setup.py +0 -0
- {hydamo_validation-1.3.0b1 → hydamo_validation-1.3.0b3}/tests/test_datasets.py +0 -0
- {hydamo_validation-1.3.0b1 → hydamo_validation-1.3.0b3}/tests/test_dommelerwaard.py +0 -0
- {hydamo_validation-1.3.0b1 → hydamo_validation-1.3.0b3}/tests/test_general_functions.py +0 -0
- {hydamo_validation-1.3.0b1 → hydamo_validation-1.3.0b3}/tests/test_hydamo_2_2.py +0 -0
- {hydamo_validation-1.3.0b1 → hydamo_validation-1.3.0b3}/tests/test_init.py +0 -0
- {hydamo_validation-1.3.0b1 → hydamo_validation-1.3.0b3}/tests/test_logic_functions.py +0 -0
- {hydamo_validation-1.3.0b1 → hydamo_validation-1.3.0b3}/tests/test_not_overlapping.py +0 -0
- {hydamo_validation-1.3.0b1 → hydamo_validation-1.3.0b3}/tests/test_productie.py +0 -0
- {hydamo_validation-1.3.0b1 → hydamo_validation-1.3.0b3}/tests/test_structures_at_interersections.py +0 -0
- {hydamo_validation-1.3.0b1 → hydamo_validation-1.3.0b3}/tests/test_summaries.py +0 -0
- {hydamo_validation-1.3.0b1 → hydamo_validation-1.3.0b3}/tests/test_topologic_functions.py +0 -0
- {hydamo_validation-1.3.0b1 → hydamo_validation-1.3.0b3}/tests/test_validationrules.py +0 -0
- {hydamo_validation-1.3.0b1 → hydamo_validation-1.3.0b3}/tests/test_wrij.py +0 -0
- {hydamo_validation-1.3.0b1 → hydamo_validation-1.3.0b3}/tests/test_wrij_profielen.py +0 -0
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
__author__ = ["Het Waterschapshuis", "D2HYDRO", "HKV", "HydroConsult"]
|
|
2
|
+
__copyright__ = "Copyright 2021, HyDAMO ValidatieTool"
|
|
3
|
+
__credits__ = ["D2HYDRO", "HKV", "HydroConsult"]
|
|
4
|
+
__version__ = "1.3.0b3"
|
|
5
|
+
|
|
6
|
+
__license__ = "MIT"
|
|
7
|
+
__maintainer__ = "Daniel Tollenaar"
|
|
8
|
+
__email__ = "daniel@d2hydro.nl"
|
|
9
|
+
|
|
10
|
+
import fiona
|
|
11
|
+
from hydamo_validation.functions import topologic as topologic_functions
|
|
12
|
+
from hydamo_validation.functions import logic as logic_functions
|
|
13
|
+
from hydamo_validation.functions import general as general_functions
|
|
14
|
+
from hydamo_validation.validator import validator
|
|
15
|
+
|
|
16
|
+
__all__ = [
|
|
17
|
+
"fiona",
|
|
18
|
+
"topologic_functions",
|
|
19
|
+
"logic_functions",
|
|
20
|
+
"general_functions",
|
|
21
|
+
"validator",
|
|
22
|
+
]
|
|
File without changes
|
|
@@ -0,0 +1,319 @@
|
|
|
1
|
+
"""functions to be executed on gdf."""
|
|
2
|
+
|
|
3
|
+
import geopandas as gpd
|
|
4
|
+
from typing import Literal
|
|
5
|
+
import numpy as np
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from rasterstats import zonal_stats
|
|
8
|
+
import logging
|
|
9
|
+
import pandas as pd
|
|
10
|
+
|
|
11
|
+
try:
|
|
12
|
+
import rasterio
|
|
13
|
+
except ImportError:
|
|
14
|
+
import gdal # noqa to avoid rasterio.version error: https://github.com/conda-forge/rasterio-feedstock/issues/240
|
|
15
|
+
import rasterio
|
|
16
|
+
|
|
17
|
+
COVERAGES = {}
|
|
18
|
+
# DATA_MODEL = None
|
|
19
|
+
# OBJECT_LAYER = None
|
|
20
|
+
|
|
21
|
+
# We get a false-positive settingwithcopywarning in buffer-function that we supress
|
|
22
|
+
pd.options.mode.chained_assignment = None
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def _set_coverage(coverage: str, directory: str):
|
|
26
|
+
"""Add a coverage for functions."""
|
|
27
|
+
global COVERAGES
|
|
28
|
+
coverage_path = Path(directory)
|
|
29
|
+
if not coverage_path.exists():
|
|
30
|
+
logging.error(
|
|
31
|
+
(
|
|
32
|
+
f"Path to coverage {coverage} does not exist: ",
|
|
33
|
+
f"{coverage_path.absolute().resolve()}",
|
|
34
|
+
". Functions using this coverage fail without data.",
|
|
35
|
+
)
|
|
36
|
+
)
|
|
37
|
+
raise FileNotFoundError(f"{coverage_path.absolute().resolve()}")
|
|
38
|
+
COVERAGES[coverage] = coverage_path
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def _buffer_row(row, column):
|
|
42
|
+
radius = max(row[column], 0.5)
|
|
43
|
+
return row.geometry.buffer(radius)
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def _get_geometric_attribute(gdf, geom_parameter):
|
|
47
|
+
geometry, method = geom_parameter.split(".")
|
|
48
|
+
return getattr(gdf[geometry], method)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def sum(gdf, array: list):
|
|
52
|
+
"""Return a sum expression."""
|
|
53
|
+
expression = " + ".join(map(str, array))
|
|
54
|
+
return gdf.eval(expression)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def difference(gdf, left, right, absolute=False):
|
|
58
|
+
"""
|
|
59
|
+
Difference between 'left' and 'right'
|
|
60
|
+
|
|
61
|
+
Parameters
|
|
62
|
+
----------
|
|
63
|
+
gdf : GeoDataFrame
|
|
64
|
+
Input GeoDataFrame
|
|
65
|
+
left : str, numeric
|
|
66
|
+
Left column or value in expression
|
|
67
|
+
right : TYPE
|
|
68
|
+
Right column or value in expression
|
|
69
|
+
absolute : bool, optional
|
|
70
|
+
Absolute (True) or relative difference (False) to left.
|
|
71
|
+
The default is False.
|
|
72
|
+
|
|
73
|
+
Returns
|
|
74
|
+
-------
|
|
75
|
+
result : Series
|
|
76
|
+
Float series
|
|
77
|
+
|
|
78
|
+
"""
|
|
79
|
+
|
|
80
|
+
if left in gdf.columns:
|
|
81
|
+
left = gdf[left]
|
|
82
|
+
if right in gdf.columns:
|
|
83
|
+
right = gdf[right]
|
|
84
|
+
if absolute:
|
|
85
|
+
result = (left - right).abs()
|
|
86
|
+
else:
|
|
87
|
+
result = left - right
|
|
88
|
+
|
|
89
|
+
return result
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def divide(gdf, left, right):
|
|
93
|
+
"""
|
|
94
|
+
Division of 'left' by 'right'
|
|
95
|
+
|
|
96
|
+
Parameters
|
|
97
|
+
----------
|
|
98
|
+
gdf : GeoDataFrame
|
|
99
|
+
Input GeoDataFrame
|
|
100
|
+
left : str, numeric
|
|
101
|
+
Left column or value in expression
|
|
102
|
+
right : TYPE
|
|
103
|
+
Right column or value in expression
|
|
104
|
+
|
|
105
|
+
Returns
|
|
106
|
+
-------
|
|
107
|
+
result : Series
|
|
108
|
+
Float series
|
|
109
|
+
|
|
110
|
+
"""
|
|
111
|
+
expression = " / ".join(map(str, [left, right]))
|
|
112
|
+
return gdf.eval(expression)
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def multiply(gdf, left, right):
|
|
116
|
+
"""
|
|
117
|
+
Multiply 'left' with 'right'
|
|
118
|
+
|
|
119
|
+
Parameters
|
|
120
|
+
----------
|
|
121
|
+
gdf : GeoDataFrame
|
|
122
|
+
Input GeoDataFrame
|
|
123
|
+
left : str, numeric
|
|
124
|
+
Left column or value in expression
|
|
125
|
+
right : str, numeric
|
|
126
|
+
Right column or value in expression
|
|
127
|
+
|
|
128
|
+
Returns
|
|
129
|
+
-------
|
|
130
|
+
result : Series
|
|
131
|
+
Float series
|
|
132
|
+
|
|
133
|
+
"""
|
|
134
|
+
expression = " * ".join(map(str, [left, right]))
|
|
135
|
+
return gdf.eval(expression)
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
def buffer(gdf, radius, percentile, coverage="ahn", fill_value: float = None):
|
|
139
|
+
"""
|
|
140
|
+
Percentile of coverage-value of an area defined by a radius around the
|
|
141
|
+
object
|
|
142
|
+
|
|
143
|
+
Parameters
|
|
144
|
+
----------
|
|
145
|
+
gdf : GeoDataFrame
|
|
146
|
+
Input GeoDataFrame
|
|
147
|
+
radius: str, numeric
|
|
148
|
+
Radius around object used to define a cirular area
|
|
149
|
+
percentile : int
|
|
150
|
+
The percentile of the coverage within area around object
|
|
151
|
+
coverage : str, optional
|
|
152
|
+
The coverage to use. The default value is 'ahn'
|
|
153
|
+
fill_value : float, optional
|
|
154
|
+
The fill_value to use when the area is not intersecting the coverage.
|
|
155
|
+
The default is None
|
|
156
|
+
|
|
157
|
+
Returns
|
|
158
|
+
-------
|
|
159
|
+
result : Series
|
|
160
|
+
Float series
|
|
161
|
+
|
|
162
|
+
"""
|
|
163
|
+
gdf_out = gdf.copy()
|
|
164
|
+
gdf_out["result"] = np.nan
|
|
165
|
+
xmin, ymin, xmax, ymax = gdf_out.total_bounds
|
|
166
|
+
coverage_path = COVERAGES[coverage]
|
|
167
|
+
|
|
168
|
+
index_gdf = gpd.read_file(coverage_path.joinpath("index.shp"))
|
|
169
|
+
|
|
170
|
+
for idx, row in index_gdf.cx[xmin:xmax, ymin:ymax].iterrows():
|
|
171
|
+
try:
|
|
172
|
+
bathymetrie_raster = coverage_path.joinpath(
|
|
173
|
+
f'{row["bladnr"].upper()}_CM.tif'
|
|
174
|
+
)
|
|
175
|
+
|
|
176
|
+
gdf_select = gdf_out.loc[
|
|
177
|
+
gdf_out["geometry"].centroid.within(row["geometry"])
|
|
178
|
+
]
|
|
179
|
+
if not gdf_select.empty:
|
|
180
|
+
if isinstance(radius, str):
|
|
181
|
+
gdf_select.loc[:, ("geometry")] = gdf_select.apply(
|
|
182
|
+
_buffer_row, args=(radius,), axis=1
|
|
183
|
+
)
|
|
184
|
+
else:
|
|
185
|
+
radius = max(radius, 0.5)
|
|
186
|
+
gdf_select.loc[:, ("geometry")] = gdf_select["geometry"].buffer(
|
|
187
|
+
radius
|
|
188
|
+
)
|
|
189
|
+
|
|
190
|
+
with rasterio.open(bathymetrie_raster, "r") as src:
|
|
191
|
+
profile = src.profile
|
|
192
|
+
raster_data = src.read(1)
|
|
193
|
+
affine = src.transform
|
|
194
|
+
scale = src.scales[0]
|
|
195
|
+
|
|
196
|
+
raster_stats = zonal_stats(
|
|
197
|
+
gdf_select,
|
|
198
|
+
raster_data,
|
|
199
|
+
affine=affine,
|
|
200
|
+
stats=f"percentile_{percentile}",
|
|
201
|
+
nodata=profile["nodata"],
|
|
202
|
+
raster_out=True,
|
|
203
|
+
)
|
|
204
|
+
|
|
205
|
+
gdf_out.loc[gdf_select.index.to_list(), "result"] = [
|
|
206
|
+
np.nan if item is None else round(item * scale, 2)
|
|
207
|
+
for item in [
|
|
208
|
+
item[f"percentile_{percentile}"] for item in raster_stats
|
|
209
|
+
]
|
|
210
|
+
]
|
|
211
|
+
except Exception as e:
|
|
212
|
+
print(
|
|
213
|
+
(
|
|
214
|
+
f"bathymetrie: {bathymetrie_raster}\n"
|
|
215
|
+
f"indices: {gdf_select.index}\n"
|
|
216
|
+
f"geometrien: {gdf_select['geometry']}"
|
|
217
|
+
)
|
|
218
|
+
)
|
|
219
|
+
raise e
|
|
220
|
+
|
|
221
|
+
# fill series if if provided
|
|
222
|
+
if fill_value is not None:
|
|
223
|
+
gdf_out.loc[gdf_out["result"].isna(), "result"] = fill_value
|
|
224
|
+
|
|
225
|
+
return gdf_out["result"]
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
def join_parameter(
|
|
229
|
+
gdf,
|
|
230
|
+
join_object: str,
|
|
231
|
+
join_gdf: gpd.GeoDataFrame,
|
|
232
|
+
join_parameter: str,
|
|
233
|
+
fill_value=None,
|
|
234
|
+
):
|
|
235
|
+
"""Joins a parameteer of other object to geodataframe."""
|
|
236
|
+
_gdf = gdf.copy()
|
|
237
|
+
|
|
238
|
+
_join_gdf = join_gdf.copy()
|
|
239
|
+
_join_gdf.set_index("globalid", inplace=True)
|
|
240
|
+
series = _join_gdf[join_parameter]
|
|
241
|
+
series.name = "result"
|
|
242
|
+
_gdf = _gdf.merge(series, how="left", left_on=f"{join_object}id", right_index=True)
|
|
243
|
+
|
|
244
|
+
# fill series if if provided
|
|
245
|
+
if fill_value is not None:
|
|
246
|
+
_gdf.loc[_gdf["result"].isna(), "result"] = fill_value
|
|
247
|
+
|
|
248
|
+
return _gdf["result"]
|
|
249
|
+
|
|
250
|
+
|
|
251
|
+
def object_relation(
|
|
252
|
+
gdf,
|
|
253
|
+
related_gdf: gpd.GeoDataFrame,
|
|
254
|
+
code_relation: str,
|
|
255
|
+
statistic: Literal["min", "max", "sum", "count"],
|
|
256
|
+
related_parameter: str = None,
|
|
257
|
+
fill_value=None,
|
|
258
|
+
):
|
|
259
|
+
"""
|
|
260
|
+
Statistic of related object to geodataframe
|
|
261
|
+
|
|
262
|
+
Parameters
|
|
263
|
+
----------
|
|
264
|
+
gdf : GeoDataFrame
|
|
265
|
+
Input GeoDataFrame
|
|
266
|
+
related_gdf : GeoDataFrame
|
|
267
|
+
GeoDataFrame with related attributes
|
|
268
|
+
code_relation : str
|
|
269
|
+
Column in related_gdf used to relate to gdf. Example 'stuwid'
|
|
270
|
+
statistic : str, options: 'min', 'max', 'sum', 'count'
|
|
271
|
+
Statistic to compute over related values
|
|
272
|
+
related_parameter: str
|
|
273
|
+
Column in related_gdf over which the statistic is to be computed
|
|
274
|
+
fill_value : float, optional
|
|
275
|
+
The fill_value to use when the area is not intersecting the coverage.
|
|
276
|
+
The default is None
|
|
277
|
+
|
|
278
|
+
Returns
|
|
279
|
+
-------
|
|
280
|
+
result : Series
|
|
281
|
+
Float series
|
|
282
|
+
|
|
283
|
+
"""
|
|
284
|
+
|
|
285
|
+
gdf_out = gdf.copy()
|
|
286
|
+
|
|
287
|
+
# remove NaN values in from related_gdf[related_parameter]
|
|
288
|
+
if related_parameter:
|
|
289
|
+
if "geometry" in related_parameter:
|
|
290
|
+
related_gdf[related_parameter] = _get_geometric_attribute(
|
|
291
|
+
related_gdf, related_parameter
|
|
292
|
+
)
|
|
293
|
+
related_gdf = related_gdf.loc[related_gdf[related_parameter].notna()]
|
|
294
|
+
|
|
295
|
+
# compute statistic
|
|
296
|
+
if statistic == "count":
|
|
297
|
+
series = related_gdf.groupby(by=[code_relation])[code_relation].count()
|
|
298
|
+
elif statistic == "sum":
|
|
299
|
+
series = related_gdf.groupby(by=[code_relation])[related_parameter].sum()
|
|
300
|
+
elif statistic == "min":
|
|
301
|
+
series = related_gdf.groupby(by=[code_relation])[related_parameter].min()
|
|
302
|
+
elif statistic == "max":
|
|
303
|
+
series = related_gdf.groupby(by=[code_relation])[related_parameter].max()
|
|
304
|
+
elif statistic == "majority":
|
|
305
|
+
series = related_gdf.groupby(by=[code_relation])[related_parameter].agg(
|
|
306
|
+
pd.Series.mode
|
|
307
|
+
)
|
|
308
|
+
|
|
309
|
+
# join series with gdf
|
|
310
|
+
series.name = "result"
|
|
311
|
+
series = pd.DataFrame(series.loc[series.index.isin(gdf["globalid"])]).reset_index()
|
|
312
|
+
gdf_out = gdf_out.merge(
|
|
313
|
+
series, how="left", left_on="globalid", right_on=code_relation
|
|
314
|
+
)
|
|
315
|
+
|
|
316
|
+
# fill series if if provided
|
|
317
|
+
if fill_value is not None:
|
|
318
|
+
gdf_out.loc[gdf_out["result"].isna(), "result"] = fill_value
|
|
319
|
+
return gdf_out["result"]
|
|
@@ -0,0 +1,338 @@
|
|
|
1
|
+
"""Logic functions to be used in eval-method."""
|
|
2
|
+
|
|
3
|
+
import pandas as pd
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def _overlapping_period(row, df, start_date, end_date):
|
|
7
|
+
_df = df[df.index != row.name]
|
|
8
|
+
return ~(
|
|
9
|
+
(row[start_date] <= _df[end_date]) & (row[end_date] >= _df[end_date])
|
|
10
|
+
).any()
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def _check_attributes(gdf, attributes):
|
|
14
|
+
for i in attributes:
|
|
15
|
+
if type(i) == str:
|
|
16
|
+
if not i in gdf.columns:
|
|
17
|
+
raise KeyError(
|
|
18
|
+
rf"'{i}' not in columns: {gdf.columns.to_list()}. Rule cannot be executed"
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def LE(gdf, left, right, dtype=bool):
|
|
23
|
+
"""
|
|
24
|
+
Evaluate if left is less or equal to/than right
|
|
25
|
+
|
|
26
|
+
Parameters
|
|
27
|
+
----------
|
|
28
|
+
gdf : GeoDataFrame
|
|
29
|
+
Input GeoDataFrame
|
|
30
|
+
left : str, numeric
|
|
31
|
+
Left column or value in expression
|
|
32
|
+
right : TYPE
|
|
33
|
+
Right column or value in expression
|
|
34
|
+
dtype : dtype, optional
|
|
35
|
+
dtype assigned to result Series
|
|
36
|
+
The default is bool.
|
|
37
|
+
|
|
38
|
+
Returns
|
|
39
|
+
-------
|
|
40
|
+
result : Series
|
|
41
|
+
Pandas Series (default dtype = bool)
|
|
42
|
+
|
|
43
|
+
"""
|
|
44
|
+
_check_attributes(gdf, [left, right])
|
|
45
|
+
expression = f"{left} <= {right}".lower()
|
|
46
|
+
return gdf.eval(expression).astype(dtype)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def LT(gdf, left, right, dtype=bool):
|
|
50
|
+
"""
|
|
51
|
+
Evaluate if left is less than right
|
|
52
|
+
|
|
53
|
+
Parameters
|
|
54
|
+
----------
|
|
55
|
+
gdf : GeoDataFrame
|
|
56
|
+
Input GeoDataFrame
|
|
57
|
+
left : str, numeric
|
|
58
|
+
Left column or value in expression
|
|
59
|
+
right : TYPE
|
|
60
|
+
Right column or value in expression
|
|
61
|
+
dtype : dtype, optional
|
|
62
|
+
dtype assigned to result Series
|
|
63
|
+
The default is bool.
|
|
64
|
+
|
|
65
|
+
Returns
|
|
66
|
+
-------
|
|
67
|
+
result : Series
|
|
68
|
+
Pandas Series (default dtype = bool)
|
|
69
|
+
|
|
70
|
+
"""
|
|
71
|
+
_check_attributes(gdf, [left, right])
|
|
72
|
+
expression = f"{left} < {right}".lower()
|
|
73
|
+
return gdf.eval(expression).astype(dtype)
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def GT(gdf, left, right, dtype=bool):
|
|
77
|
+
"""
|
|
78
|
+
Evaluate if left is greater than right
|
|
79
|
+
|
|
80
|
+
Parameters
|
|
81
|
+
----------
|
|
82
|
+
gdf : GeoDataFrame
|
|
83
|
+
Input GeoDataFrame
|
|
84
|
+
left : str, numeric
|
|
85
|
+
Left column or value in expression
|
|
86
|
+
right : TYPE
|
|
87
|
+
Right column or value in expression
|
|
88
|
+
dtype : dtype, optional
|
|
89
|
+
dtype assigned to result Series
|
|
90
|
+
The default is bool.
|
|
91
|
+
|
|
92
|
+
Returns
|
|
93
|
+
-------
|
|
94
|
+
result : Series
|
|
95
|
+
Pandas Series (default dtype = bool)
|
|
96
|
+
|
|
97
|
+
"""
|
|
98
|
+
_check_attributes(gdf, [left, right])
|
|
99
|
+
expression = f"{left} > {right}".lower()
|
|
100
|
+
return gdf.eval(expression).astype(dtype)
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def GE(gdf, left, right, dtype=bool):
|
|
104
|
+
"""Evaluate if left is greater or equal to/than right
|
|
105
|
+
|
|
106
|
+
Parameters
|
|
107
|
+
----------
|
|
108
|
+
gdf : GeoDataFrame
|
|
109
|
+
Input GeoDataFrame
|
|
110
|
+
left : str, numeric
|
|
111
|
+
Left column or value in expression
|
|
112
|
+
right : TYPE
|
|
113
|
+
Right column or value in expression
|
|
114
|
+
dtype : dtype, optional
|
|
115
|
+
dtype assigned to result Series
|
|
116
|
+
The default is bool.
|
|
117
|
+
|
|
118
|
+
Returns
|
|
119
|
+
-------
|
|
120
|
+
result : Series
|
|
121
|
+
Pandas Series (default dtype = bool)
|
|
122
|
+
|
|
123
|
+
"""
|
|
124
|
+
_check_attributes(gdf, [left, right])
|
|
125
|
+
expression = f"{left} >= {right}".lower()
|
|
126
|
+
return gdf.eval(expression).astype(dtype)
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def EQ(gdf, left, right, dtype=bool):
|
|
130
|
+
"""Evalate if left an right expression are equal
|
|
131
|
+
|
|
132
|
+
Parameters
|
|
133
|
+
----------
|
|
134
|
+
gdf : GeoDataFrame
|
|
135
|
+
Input GeoDataFrame
|
|
136
|
+
left : str, numeric
|
|
137
|
+
Left column or value in expression
|
|
138
|
+
right : TYPE
|
|
139
|
+
Right column or value in expression
|
|
140
|
+
dtype : dtype, optional
|
|
141
|
+
dtype assigned to result Series
|
|
142
|
+
The default is bool.
|
|
143
|
+
|
|
144
|
+
Returns
|
|
145
|
+
-------
|
|
146
|
+
result : Series
|
|
147
|
+
Pandas Series (default dtype = bool)
|
|
148
|
+
|
|
149
|
+
"""
|
|
150
|
+
_check_attributes(gdf, [left, right])
|
|
151
|
+
expression = f"{left} == {right}".lower()
|
|
152
|
+
return gdf.eval(expression).astype(dtype)
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
def BE(gdf, parameter, min, max, inclusive=False):
|
|
156
|
+
"""Evaluate if parameter-value is between min/max inclusive (true/false)
|
|
157
|
+
|
|
158
|
+
Parameters
|
|
159
|
+
----------
|
|
160
|
+
gdf : GeoDataFrame
|
|
161
|
+
Input GeoDataFrame
|
|
162
|
+
parameter: str
|
|
163
|
+
Input column with numeric values
|
|
164
|
+
min : numeric
|
|
165
|
+
Lower limit of function
|
|
166
|
+
max : numeric
|
|
167
|
+
Upper limit of function
|
|
168
|
+
inclusive : bool, optional
|
|
169
|
+
To include min and max
|
|
170
|
+
The default is False.
|
|
171
|
+
|
|
172
|
+
Returns
|
|
173
|
+
-------
|
|
174
|
+
result : Series
|
|
175
|
+
Pandas Series (default dtype = bool)
|
|
176
|
+
|
|
177
|
+
"""
|
|
178
|
+
_check_attributes(gdf, [parameter, min, max])
|
|
179
|
+
if inclusive:
|
|
180
|
+
series = GE(gdf, parameter, min, dtype=bool) & LE(
|
|
181
|
+
gdf, parameter, max, dtype=bool
|
|
182
|
+
)
|
|
183
|
+
else:
|
|
184
|
+
series = GT(gdf, parameter, min, dtype=bool) & LT(
|
|
185
|
+
gdf, parameter, max, dtype=bool
|
|
186
|
+
)
|
|
187
|
+
return series
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
def ISIN(gdf, parameter, array):
|
|
191
|
+
"""Evaluate if values in parameter are in array
|
|
192
|
+
|
|
193
|
+
Parameters
|
|
194
|
+
----------
|
|
195
|
+
gdf : GeoDataFrame
|
|
196
|
+
Input GeoDataFrame
|
|
197
|
+
parameter: str
|
|
198
|
+
Input column with numeric values
|
|
199
|
+
array : list
|
|
200
|
+
list of possible values that return True
|
|
201
|
+
|
|
202
|
+
Returns
|
|
203
|
+
-------
|
|
204
|
+
result : Series
|
|
205
|
+
Pandas Series (default dtype = bool)
|
|
206
|
+
|
|
207
|
+
"""
|
|
208
|
+
_check_attributes(gdf, [parameter])
|
|
209
|
+
return gdf[parameter].isin(array)
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
def NOTIN(gdf, parameter, array):
|
|
213
|
+
"""Evaluate if values in parameter are not in array
|
|
214
|
+
|
|
215
|
+
Parameters
|
|
216
|
+
----------
|
|
217
|
+
gdf : GeoDataFrame
|
|
218
|
+
Input GeoDataFrame
|
|
219
|
+
parameter: str
|
|
220
|
+
Input column with numeric values
|
|
221
|
+
array : list
|
|
222
|
+
list of possible values that return False
|
|
223
|
+
|
|
224
|
+
Returns
|
|
225
|
+
-------
|
|
226
|
+
result : Series
|
|
227
|
+
Pandas Series (default dtype = bool)
|
|
228
|
+
|
|
229
|
+
"""
|
|
230
|
+
_check_attributes(gdf, [parameter])
|
|
231
|
+
return ~ISIN(gdf, parameter, array)
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
def NOTNA(gdf, parameter):
|
|
235
|
+
"""Evaluate if values in parameter ar not NaN or None
|
|
236
|
+
|
|
237
|
+
Parameters
|
|
238
|
+
----------
|
|
239
|
+
gdf : GeoDataFrame
|
|
240
|
+
Input GeoDataFrame
|
|
241
|
+
parameter: str
|
|
242
|
+
Input column with numeric values
|
|
243
|
+
|
|
244
|
+
Returns
|
|
245
|
+
-------
|
|
246
|
+
result : Series
|
|
247
|
+
Pandas Series (default dtype = bool)
|
|
248
|
+
|
|
249
|
+
"""
|
|
250
|
+
_check_attributes(gdf, [parameter])
|
|
251
|
+
return gdf[parameter].notna()
|
|
252
|
+
|
|
253
|
+
|
|
254
|
+
def join_object_exists(gdf, join_gdf, join_object):
|
|
255
|
+
"""Evaluate if defined related_object id exists in globalid parameter of
|
|
256
|
+
related object-table.
|
|
257
|
+
|
|
258
|
+
Parameters
|
|
259
|
+
----------
|
|
260
|
+
gdf : GeoDataFrame
|
|
261
|
+
Input GeoDataFrame
|
|
262
|
+
related_gdf : GeoDataFrame
|
|
263
|
+
Input GeoDataFrame with related objects
|
|
264
|
+
object: str
|
|
265
|
+
HyDAMO object name of related object-layer
|
|
266
|
+
|
|
267
|
+
Returns
|
|
268
|
+
-------
|
|
269
|
+
result : Series
|
|
270
|
+
Pandas Series (default dtype = bool)
|
|
271
|
+
|
|
272
|
+
"""
|
|
273
|
+
_check_attributes(join_gdf, ["globalid"])
|
|
274
|
+
_check_attributes(gdf, [f"{join_object}id"])
|
|
275
|
+
return gdf[f"{join_object}id"].isin(join_gdf["globalid"])
|
|
276
|
+
|
|
277
|
+
|
|
278
|
+
def consistent_period(
|
|
279
|
+
gdf,
|
|
280
|
+
max_gap=1,
|
|
281
|
+
groupers=["pompid", "regelmiddelid"],
|
|
282
|
+
priority="prioriteit",
|
|
283
|
+
start_date="beginperiode",
|
|
284
|
+
date_format="%d%m",
|
|
285
|
+
end_date="eindperiode",
|
|
286
|
+
):
|
|
287
|
+
"""Check if a periodic-based table is time-consistent
|
|
288
|
+
|
|
289
|
+
Parameters
|
|
290
|
+
----------
|
|
291
|
+
gdf : GeoDataFrame
|
|
292
|
+
Input GeoDataFrame
|
|
293
|
+
max_gap: int
|
|
294
|
+
max gap in days between too adjacent periods
|
|
295
|
+
|
|
296
|
+
Returns
|
|
297
|
+
-------
|
|
298
|
+
result : Series
|
|
299
|
+
Pandas Series (default dtype = bool)
|
|
300
|
+
|
|
301
|
+
"""
|
|
302
|
+
|
|
303
|
+
# create an empty result
|
|
304
|
+
_gdf = gdf.copy()
|
|
305
|
+
result = pd.Series(index=_gdf.index)
|
|
306
|
+
|
|
307
|
+
# convert start_parameter and end_parameter to datetime
|
|
308
|
+
_gdf[start_date] = pd.to_datetime(_gdf[start_date], format=date_format)
|
|
309
|
+
_gdf[end_date] = pd.to_datetime(_gdf[end_date], format=date_format)
|
|
310
|
+
|
|
311
|
+
index_select = _gdf[start_date] > _gdf[end_date]
|
|
312
|
+
_gdf.loc[index_select, end_date] = _gdf[index_select][
|
|
313
|
+
end_date
|
|
314
|
+
] + pd.offsets.DateOffset(years=1)
|
|
315
|
+
|
|
316
|
+
for group in groupers:
|
|
317
|
+
grouper = _gdf.groupby(by=[group, "prioriteit"])
|
|
318
|
+
|
|
319
|
+
for _, df in _gdf.groupby(by=["pompid", "prioriteit"]):
|
|
320
|
+
df.sort_values(by=start_date, inplace=True)
|
|
321
|
+
|
|
322
|
+
# check for overlap
|
|
323
|
+
bool_series = df.apply(
|
|
324
|
+
(lambda x: _overlapping_period(x, df, start_date, end_date)), axis=1
|
|
325
|
+
)
|
|
326
|
+
|
|
327
|
+
# check for gaps
|
|
328
|
+
gaps_series = df[start_date] - df.shift(1)[end_date]
|
|
329
|
+
gaps_series.iloc[0] = pd.Timedelta(days=0) # due to shift we have NaT here
|
|
330
|
+
|
|
331
|
+
# add to result
|
|
332
|
+
bool_series = (gaps_series <= pd.Timedelta(days=int(max_gap))) & bool_series
|
|
333
|
+
bool_series = bool_series[
|
|
334
|
+
bool_series.index.isin(result[result.isna() | (result == True)].index)
|
|
335
|
+
]
|
|
336
|
+
result.loc[result.index.isin(bool_series.index)] = bool_series
|
|
337
|
+
|
|
338
|
+
return result
|