Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
60 commits
Select commit Hold shift + click to select a range
1db4a59
new waterwebservice beta urls, added ProcessType to catalog, catched …
veenstrajelmer Oct 7, 2024
1037f92
fixed some testcases
veenstrajelmer Oct 7, 2024
f295b82
remove valueerror again, was a mistake
veenstrajelmer Oct 8, 2024
df65492
Merge branch 'main' into new-waterwebservices
veenstrajelmer Oct 8, 2024
9ef79e3
fixed nodataerror
veenstrajelmer Oct 8, 2024
ef3aea2
updated endpoints.json
veenstrajelmer Oct 9, 2024
601594c
updated test
veenstrajelmer Oct 9, 2024
3d0a1ce
fixed cli test
veenstrajelmer Oct 9, 2024
f9adf58
fixed cli test
veenstrajelmer Oct 9, 2024
d4a9cad
updated comment
veenstrajelmer Oct 9, 2024
404cd76
updated comment
veenstrajelmer Oct 9, 2024
596c00c
better request error handling
veenstrajelmer Oct 10, 2024
f7e1799
cleanups
veenstrajelmer Oct 10, 2024
ea58fa1
added procestype
veenstrajelmer Oct 10, 2024
a2313a8
fixed cli testcase
veenstrajelmer Oct 10, 2024
7535248
cli properly
veenstrajelmer Oct 10, 2024
ff50d43
resolved conflicts
veenstrajelmer Oct 10, 2024
a1bf59a
resolved conflicts
veenstrajelmer Oct 10, 2024
cf06bc5
Merge branch 'main' into new-waterwebservices
veenstrajelmer Oct 10, 2024
6ee9d7a
all tests green
veenstrajelmer Oct 10, 2024
6374273
updated docs
veenstrajelmer Oct 11, 2024
d9202cc
updated comment
veenstrajelmer Oct 11, 2024
9d3db60
Merge branch 'main' into new-waterwebservices
veenstrajelmer Oct 30, 2024
6dbd3ea
Merge branch 'main' into new-waterwebservices
veenstrajelmer Apr 10, 2025
f9e9133
Merge branch 'main' into new-waterwebservices
veenstrajelmer May 20, 2025
c05beb0
Merge branch 'main' into new-waterwebservices
veenstrajelmer Jun 19, 2025
c3cbe10
remove obsolete flattening of metadata
veenstrajelmer Jun 19, 2025
920b0f5
fixed codesmell
veenstrajelmer Jun 19, 2025
a68cecb
Merge branch 'main' into new-waterwebservices
veenstrajelmer Nov 26, 2025
fe82df0
update to new ddapi20 url
veenstrajelmer Dec 5, 2025
badb695
cleaned up todo
veenstrajelmer Dec 5, 2025
7246bab
cleaned up todo
veenstrajelmer Dec 5, 2025
579f1e4
cleaned up todo
veenstrajelmer Dec 5, 2025
3672f7f
revert expected values
veenstrajelmer Dec 5, 2025
264e334
revert expected values
veenstrajelmer Dec 5, 2025
5cb8a16
reverted expected values
veenstrajelmer Dec 5, 2025
e13f20e
fix tests
veenstrajelmer Dec 5, 2025
00022c4
cleaned up todo
veenstrajelmer Dec 5, 2025
98f26d5
updated measurements notebook
veenstrajelmer Dec 5, 2025
a088b43
fixed conflict
veenstrajelmer Dec 8, 2025
947dfe6
added issue in comment
veenstrajelmer Dec 10, 2025
822d25c
cleaned up todo comment
veenstrajelmer Dec 10, 2025
97407c9
updated TODO comments
veenstrajelmer Dec 10, 2025
9951efd
updated TODO comments
veenstrajelmer Dec 10, 2025
2db4867
updated TODO comments
veenstrajelmer Dec 10, 2025
ad866d4
updated TODO comments
veenstrajelmer Dec 10, 2025
ba08a1f
updated TODO comments
veenstrajelmer Dec 10, 2025
ee08ab7
updated TODO comments
veenstrajelmer Dec 10, 2025
2524e3b
Merge branch 'main' into new-waterwebservices
veenstrajelmer Dec 10, 2025
b337bf1
made _send_post_request() more robust by try/except around resp.json()
veenstrajelmer Dec 10, 2025
e7c5162
added comment
veenstrajelmer Dec 10, 2025
249e5c4
added comment
veenstrajelmer Dec 10, 2025
66f57c7
updated example notebooks
veenstrajelmer Dec 10, 2025
779b083
updated changelog
veenstrajelmer Dec 10, 2025
9cc0aa1
proper error handling in _ send_post_request()
veenstrajelmer Dec 10, 2025
8c4feb4
add tests and clean up code
veenstrajelmer Dec 10, 2025
aca0161
removed check for result["Succesvol"]
veenstrajelmer Dec 10, 2025
d9e10bd
clean up code
veenstrajelmer Dec 10, 2025
8599b43
added issue to comment
veenstrajelmer Dec 10, 2025
382d90e
resolved sonarqube todos
veenstrajelmer Dec 10, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions HISTORY.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ History

UNRELEASED
----------
* updated to new waterwebservices (ddapi20-waterwebservices.rijkswaterstaat.nl) in https://github.com/Deltares/ddlpy/pull/124

0.7.0 (2025-06-12)
------------------
Expand Down
20 changes: 13 additions & 7 deletions ddlpy/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,11 @@ def cli(verbose, args=None):
help='Station codes, e.g. HOEKVHLD',
multiple=True
)
@click.option(
'--procestype',
help='Procestype, e.g. meting, astronomisch, verwachting',
multiple=True
)
@click.option(
'--grootheid-code',
help='Grootheid code, e.g. WATHTE',
Expand Down Expand Up @@ -69,6 +74,7 @@ def cli(verbose, args=None):
)
def locations(output,
station,
procestype,
grootheid_code,
groepering_code,
hoedanigheid_code,
Expand All @@ -82,7 +88,8 @@ def locations(output,
locations_df = ddlpy.locations()

stations = station
quantities = {'Grootheid.Code': list(grootheid_code),
quantities = {'ProcesType':list(procestype),
'Grootheid.Code': list(grootheid_code),
'Groepering.Code': list(groepering_code),
'Hoedanigheid.Code': list(hoedanigheid_code),
'Eenheid.Code': list(eenheid_code),
Expand Down Expand Up @@ -128,24 +135,23 @@ def measurements(locations, start_date, end_date):
except:
raise ValueError('locations.json file not found. First run "ddlpy locations"')

for obs in range(locations_df.shape[0]): #goes through rows in table
selected = locations_df.loc[obs]

for irow, selected in locations_df.iterrows(): #goes through rows in table
measurements = ddlpy.measurements(
selected, start_date=start_date, end_date=end_date)

if (len(measurements) > 0):
if len(measurements) > 0:
print('Measurements of %s were obtained' % selected['Code'])
station = selected['Code']
pt = selected['ProcesType']
cc = selected['Compartiment.Code']
ec = selected['Eenheid.Code']
gc = selected['Grootheid.Code']
grc = selected['Groepering.Code']
hc = selected['Hoedanigheid.Code']
pc = selected['Parameter.Code']

measurements.to_csv('%s_%s_%s_%s_%s_%s_%s.csv' %
(station, cc, ec, gc, grc, hc, pc))
measurements.to_csv('%s_%s_%s_%s_%s_%s_%s_%s.csv' %
(station, pt ,cc, ec, gc, grc, hc, pc))
else:
print('No Data of station %s were retrieved from Water Info' %
selected['Code'])
Expand Down
55 changes: 22 additions & 33 deletions ddlpy/ddlpy.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

BASE_URL = "https://waterwebservices.rijkswaterstaat.nl/"
ENDPOINTS_PATH = pathlib.Path(__file__).with_name("endpoints.json")
logger = logging.getLogger(__name__)

with ENDPOINTS_PATH.open() as f:
ENDPOINTS = json.load(f)
Expand All @@ -25,36 +26,21 @@ class NoDataError(ValueError):
pass


class UnsuccessfulRequestError(ValueError):
pass


# Web Feature Service
# Web Mapping Service
logger = logging.getLogger(__name__)


def _send_post_request(url, request, timeout=None):
logger.debug("Requesting at {} with request: {}".format(url, json.dumps(request)))
resp = requests.post(url, json=request, timeout=timeout)

if not resp.ok:
raise IOError("Request failed: {}".format(resp.text))
# in case of for instance
# resp.status_code: 400, resp.reason: Bad Request, resp.text: {"Succesvol":false,"Foutmelding":"Het maximaal aantal waarnemingen (160000) is overschreden. Beperk uw request.","WaarnemingenLijst":[]}
# resp.status_code: 500, resp.reason: Internal Server Error
raise IOError(f"{resp.status_code} {resp.reason}: {resp.text}")

result = resp.json()
if not result['Succesvol']:
logger.debug('Response result is unsuccessful: {}'.format(result))
error_message = result.get('Foutmelding', 'No error returned')
if error_message == "Geen gegevens gevonden!":
# Foutmelding: "Geen gegevens gevonden!"
# this is a valid response for periods where there is no data
# this error is raised here, but catched in ddlpy.ddlpy.measurements() so the process can continue.
raise NoDataError(error_message)
else:
# Foutmelding: "Het max aantal waarnemingen (157681) is overschreven, beperk uw request."
# or any other possible error message are raised here
raise UnsuccessfulRequestError(error_message)
if resp.status_code==204:
# "204 No Content" is raised here, but catched in ddlpy.ddlpy.measurements() so the process can continue.
raise NoDataError(f"{resp.status_code} {resp.reason}: {resp.text}")

# continue if request was successful
result = resp.json()
return result


Expand Down Expand Up @@ -139,13 +125,14 @@ def _get_request_dicts(location):
# generate aquometadata dict from location "*.Code" values
key_list = [x.replace(".Code","") for x in location.index if x.endswith(".Code")]
aquometadata_dict = {key:{"Code":location[f"{key}.Code"]} for key in key_list}
# additional code required for ProcesType since this does not adhere to the
# Code/Omschrijving convention.
if "ProcesType" in location.index:
aquometadata_dict["ProcesType"] = location["ProcesType"]

# generate location dict from relevant values
locatie_dict = {
"X": location["X"],
"Y": location["Y"],
# assert code is used as index
# TODO: use a numpy compatible json encoder in requests
"Code": location.get("Code", location.name),
}

Expand Down Expand Up @@ -261,7 +248,10 @@ def measurements_amount(location:pd.Series, start_date:(str,pd.Timestamp), end_d
df = df.set_index("Groeperingsperiode")
df = df[["AantalMetingen"]]
df_list.append(df)


if len(df_list) == 0:
raise NoDataError("no measurements available returned")

# concatenate and sum duplicated index
df_amount = pd.concat(df_list).sort_index()
df_amount = df_amount.groupby(df_amount.index).sum()
Expand All @@ -279,8 +269,7 @@ def _combine_waarnemingenlijst(result, location):
new_row = {}
for key, value in row["WaarnemingMetadata"].items():
new_key = "WaarnemingMetadata." + key
new_val = value[0] if len(value) == 1 else value
new_row[new_key] = new_val
new_row[new_key] = value

# add remaining data
for key, val in row.items():
Expand All @@ -289,7 +278,7 @@ def _combine_waarnemingenlijst(result, location):
new_row[key] = val

# add metadata
for key, val in list(waarneming["AquoMetadata"].items()):
for key, val in waarneming["AquoMetadata"].items():
if isinstance(val, dict) and "Code" in val and "Omschrijving" in val:
# some values have a code/omschrijving pair, flatten them
new_key = key + ".Code"
Expand All @@ -311,8 +300,8 @@ def _combine_waarnemingenlijst(result, location):
for name in [
"Coordinatenstelsel",
"Naam",
"X",
"Y",
"Lon",
"Lat",
]:
df[name] = location[name]

Expand Down
41 changes: 14 additions & 27 deletions ddlpy/endpoints.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"collect_catalogue": {
"name": "OphalenCatalogus",
"url": "https://waterwebservices.rijkswaterstaat.nl/METADATASERVICES_DBO/OphalenCatalogus",
"url": "https://ddapi20-waterwebservices.rijkswaterstaat.nl/METADATASERVICES/OphalenCatalogus",
"type": "POST",
"request": {
"CatalogusFilter": {
Expand All @@ -10,20 +10,19 @@
"Hoedanigheden": true,
"Groeperingen": true,
"Parameters": true,
"ProcesTypes": true,
"Compartimenten": true
}
}
},
"collect_observations": {
"name": "OphalenWaarnemingen",
"url": "https://waterwebservices.rijkswaterstaat.nl/ONLINEWAARNEMINGENSERVICES_DBO/OphalenWaarnemingen",
"url": "https://ddapi20-waterwebservices.rijkswaterstaat.nl/ONLINEWAARNEMINGENSERVICES/OphalenWaarnemingen",
"type": "POST",
"_comment": "Make sure you specify the request location to the exact number of decimals and the time in the exact format (samen number of zeros).",
"request": {
"Locatie": {
"X": 761899.770959577,
"Y": 5915790.48491405,
"Code": "DELFZL"
"Code": "delfzijl"
},
"AquoPlusWaarnemingMetadata": {
"AquoMetadata": {
Expand All @@ -46,7 +45,7 @@
},
"collect_latest_observations": {
"name": "OphalenLaatsteWaarnemingen",
"url": "https://waterwebservices.rijkswaterstaat.nl/ONLINEWAARNEMINGENSERVICES_DBO/OphalenLaatsteWaarnemingen",
"url": "https://ddapi20-waterwebservices.rijkswaterstaat.nl/ONLINEWAARNEMINGENSERVICES/OphalenLaatsteWaarnemingen",
"type": "POST",
"_comment": "Make sure you specify the request location to the exact number of decimals.",
"request": {
Expand All @@ -67,16 +66,14 @@
],
"LocatieLijst": [
{
"X": 518882.333320247,
"Y": 5760829.11729589,
"Code": "EURPFM"
"Code": "europlatform"
}
]
}
},
"check_observations_available": {
"name": "CheckWaarnemingenAanwezig",
"url": "https://waterwebservices.rijkswaterstaat.nl/ONLINEWAARNEMINGENSERVICES_DBO/CheckWaarnemingenAanwezig",
"url": "https://ddapi20-waterwebservices.rijkswaterstaat.nl/ONLINEWAARNEMINGENSERVICES/CheckWaarnemingenAanwezig",
"type": "POST",
"_comment": "Make sure you specify the request location to the exact number of decimals and the time in the exact format (samen number of zeros).",
"request": {
Expand All @@ -92,9 +89,7 @@
],
"LocatieLijst": [
{
"X": 518882.333320247,
"Y": 5760829.11729589,
"Code": "EURPFM"
"Code": "europlatform"
}
],
"Periode": {
Expand All @@ -105,7 +100,7 @@
},
"collect_number_of_observations": {
"name": "OphalenAantalWaarnemingen",
"url": "https://waterwebservices.rijkswaterstaat.nl/ONLINEWAARNEMINGENSERVICES_DBO/OphalenAantalWaarnemingen",
"url": "https://ddapi20-waterwebservices.rijkswaterstaat.nl/ONLINEWAARNEMINGENSERVICES/OphalenAantalWaarnemingen",
"type": "POST",
"request": {
"AquoMetadataLijst": [
Expand All @@ -117,9 +112,7 @@
"Groeperingsperiode": "Week",
"LocatieLijst": [
{
"X": 518882.333320247,
"Y": 5760829.11729589,
"Code": "EURPFM"
"Code": "europlatform"
}
],
"Periode": {
Expand All @@ -130,7 +123,7 @@
},
"request_bulk_observations": {
"name": "AanvragenBulkWaarnemingen",
"url": "https://waterwebservices.rijkswaterstaat.nl/BULKWAARNEMINGSERVICES_DBO/AanvragenBulkWaarnemingen",
"url": "https://ddapi20-waterwebservices.rijkswaterstaat.nl/BULKWAARNEMINGSERVICES/AanvragenBulkWaarnemingen",
"type": "POST",
"_comment": "Make sure you specify the request location to the exact number of decimals and the time in the exact format (samen number of zeros).",
"request": {
Expand Down Expand Up @@ -172,19 +165,13 @@
],
"LocatieLijst": [
{
"X": 742469.913149676,
"Y": 5940708.14824459,
"Code": "HUIBGOT"
"Code": "huibertgat.oost"
},
{
"X": 595875.376191307,
"Y": 5790952.82210343,
"Code": "NOORDWK2"
"Code": "noordwijk.2kmuitdekust.flachsee"
},
{
"X": 571670.054611366,
"Y": 5822651.05560318,
"Code": "IJMDMNTSPS"
"Code": "ijmuiden.munitiestort.1"
}
],
"Periode": {
Expand Down
2 changes: 1 addition & 1 deletion ddlpy/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ def dataframe_to_xarray(df: pd.DataFrame, drop_if_constant=[]):
cols_onlynvt_code = [x for x in cols_onlynvt_code if x.endswith(".Code")]

# create list of location columns, will be dropped (added as ds.attrs)
cols_location = ['Code', 'Naam', 'Coordinatenstelsel', 'X', 'Y']
cols_location = ['Code', 'Naam', 'Coordinatenstelsel', 'Lon', 'Lat']

# add drop_if_constant colums to list if values are indeed constant, will be dropped (added as ds.attrs)
cols_constant = []
Expand Down
11 changes: 7 additions & 4 deletions docs/examples/minimal_example.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,17 @@

#select a set of parameters
# Filter the locations dataframe with the desired parameters and stations.
bool_stations = locations.index.isin(['IJMDBTHVN', 'DANTZGZD','HOEKVHLD'])
# measured (WATHTE) versus computed/astro
bool_stations = locations.index.isin(['ijmuiden.buitenhaven', 'dantziggat.zuid', 'hoekvanholland', 'ameland.nes'])
# meting/astronomisch/verwachting
bool_procestype = locations['ProcesType'].isin(['meting'])
# waterlevel/waterhoogte (WATHTE)
bool_grootheid = locations['Grootheid.Code'].isin(['WATHTE'])
# timeseries (NVT) versus extremes
bool_groepering = locations['Groepering.Code'].isin(['NVT'])
bool_groepering = locations['Groepering.Code'].isin([''])
# vertical reference (NAP/MSL)
bool_hoedanigheid = locations['Hoedanigheid.Code'].isin(['NAP'])
selected = locations.loc[bool_stations & bool_grootheid &
selected = locations.loc[bool_procestype &
bool_stations & bool_grootheid &
bool_groepering & bool_hoedanigheid]

start_date = dt.datetime(2023, 1, 1)
Expand Down
15 changes: 6 additions & 9 deletions docs/examples/retrieve_parallel_to_netcdf.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@


def get_data(location, start_date, end_date, dir_output, overwrite=True):

station_id = location.name
station_messageid = location["Locatie_MessageID"]
filename = os.path.join(dir_output, f"{station_id}-{station_messageid}.nc")
Expand Down Expand Up @@ -44,19 +43,18 @@ def get_data(location, start_date, end_date, dir_output, overwrite=True):
ds.to_netcdf(filename, format="NETCDF4_CLASSIC")


if ( __name__ == "__main__" ):

if __name__ == "__main__":
dir_output = './ddl_retrieved_data'
os.makedirs(dir_output, exist_ok=True)

# get locations
locations = ddlpy.locations()
bool_stations = locations.index.isin(['IJMDBTHVN', 'DANTZGZD', 'HOEKVHLD', 'VLISSGN', 'HOEK', 'VLIS', "OLST"])
bool_grootheid = locations['Grootheid.Code'].isin(['WATHTE']) # measured (WATHTE) versus computed/astro
bool_groepering = locations['Groepering.Code'].isin(['NVT']) # timeseries (NVT) versus extremes
bool_stations = locations.index.isin(['ijmuiden.buitenhaven', 'dantziggat.zuid', 'hoekvanholland', 'ameland.nes', 'vlissingen', 'olst'])
bool_procestype = locations['ProcesType'].isin(['meting']) # meting/astronomisch/verwachting
bool_grootheid = locations['Grootheid.Code'].isin(['WATHTE']) # waterlevel (WATHTE)
bool_groepering = locations['Groepering.Code'].isin(['']) # timeseries (NVT) versus extremes
bool_hoedanigheid = locations['Hoedanigheid.Code'].isin(['NAP']) # vertical reference (NAP/MSL)
selected = locations.loc[bool_stations & bool_grootheid & bool_groepering & bool_hoedanigheid]

selected = locations.loc[bool_stations & bool_procestype & bool_grootheid & bool_groepering & bool_hoedanigheid]

start_date = dt.datetime(2022, 1, 1)
end_date = dt.datetime(2022, 3, 1)
Expand All @@ -70,7 +68,6 @@ def get_data(location, start_date, end_date, dir_output, overwrite=True):
for station_code, location in selected.iterrows():
executor.submit(get_data, location, start_date, end_date, dir_output)


file_list = glob.glob(os.path.join(dir_output, "*.nc"))
fig, ax = plt.subplots()
for file_nc in file_list:
Expand Down
Loading
Loading