{"id":2126,"date":"2024-02-15T08:50:32","date_gmt":"2024-02-15T07:50:32","guid":{"rendered":"https:\/\/www.pa3hcm.nl\/?p=2126"},"modified":"2024-02-15T09:05:27","modified_gmt":"2024-02-15T08:05:27","slug":"cli-for-dutch-water-data","status":"publish","type":"post","link":"https:\/\/www.pa3hcm.nl\/?p=2126","title":{"rendered":"CLI for dutch water data"},"content":{"rendered":"\n<figure class=\"wp-block-image alignright size-medium\"><a href=\"https:\/\/www.pa3hcm.nl\/wp-content\/uploads\/2024\/02\/wtr-output.png\"><img loading=\"lazy\" decoding=\"async\" width=\"300\" height=\"169\" src=\"https:\/\/www.pa3hcm.nl\/wp-content\/uploads\/2024\/02\/wtr-output-300x169.png\" alt=\"\" class=\"wp-image-2127\" srcset=\"https:\/\/www.pa3hcm.nl\/wp-content\/uploads\/2024\/02\/wtr-output-300x169.png 300w, https:\/\/www.pa3hcm.nl\/wp-content\/uploads\/2024\/02\/wtr-output-1024x576.png 1024w, https:\/\/www.pa3hcm.nl\/wp-content\/uploads\/2024\/02\/wtr-output-768x432.png 768w, https:\/\/www.pa3hcm.nl\/wp-content\/uploads\/2024\/02\/wtr-output-1536x864.png 1536w, https:\/\/www.pa3hcm.nl\/wp-content\/uploads\/2024\/02\/wtr-output.png 1920w\" sizes=\"auto, (max-width: 300px) 100vw, 300px\" \/><\/a><\/figure>\n\n\n\n<p>Here in the Netherlands we have a public organisation named <em>Rijkswaterstaat<\/em>. This organization keeps track of all the water in and around our country. Think about sea levels, locks in rivers, etc. To do so they have an extensive network of sensors around the country, monitoring water levels, current speed, temperatures, etc. I wrote a command line interface to access this data.<\/p>\n\n\n\n<!--more-->\n\n\n\n<p>People living in The Netherlands are always curious about water. Although there is a high level of trust in all the waterworks, they are always just a bit more alert when a storm smashes the sea water against the dams and dunes, or when river tides gets too low to allow transport over water. They know how important these structures are, since a large part of the country is below sea level.<\/p>\n\n\n\n<p>The dutch government has a ministry  and a couple of organisations that deal with all this. One of the most important organisations is <a href=\"https:\/\/en.wikipedia.org\/wiki\/Rijkswaterstaat\" target=\"_blank\" rel=\"noreferrer noopener\">Rijkswaterstaat<\/a>, which manages (according to Wikipedia) &#8220;public works and water management, including the construction and maintenance of waterways and roads, and flood protection and prevention&#8221;.<\/p>\n\n\n\n<p>One of the services of Rijkswaterstaat is running a large network of sensors to monitor the water in and around the country. All data of these sensors is publicly available, and Rijkswaterstaat also publishes this on their <a href=\"https:\/\/waterinfo.rws.nl\/\" target=\"_blank\" rel=\"noreferrer noopener\">Waterinfo<\/a> website, with nice maps and graphs. <\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Command line interface<\/h2>\n\n\n\n<p>The graphical representation is not really accessible for blind people, some of them prefer the &#8220;raw&#8221; data in text tables so see how water levels change in time. One of my friends is blind AND interested in this data, so I decided to build him a nice CLI (command line interface). This was rather easy to build, since the data is also available by means of a nice <a href=\"https:\/\/rijkswaterstaatdata.nl\/waterdata\/\" target=\"_blank\" rel=\"noreferrer noopener\">API<\/a>. I named the tool <em>wtr<\/em>, it&#8217;s written in Python and the code is open source (GPL). You can find it on <a href=\"https:\/\/github.com\/pa3hcm\/wtr\" target=\"_blank\" rel=\"noreferrer noopener\">GitHub<\/a>. The code is tested on Linux, but will probably run on any operating system that can run Python.<\/p>\n\n\n\n<p>I&#8217;m actually quite happy with the code, since it is probably my most professionally written Python code so far. I used tools like <code>pylint<\/code> and <code>black<\/code> to ensure the code syntaxt and formatting. On GitHub I have configured GitHub Actions which run <code>pylint<\/code> for miscellaneous Python versions and do some functional tests on the code as well. I used <em>Visual Studio Code<\/em> to write the code, with some help of <em>GitHub Copilot<\/em>. This is a gen-AI tool that acts like a second developer sitting next to you, helping you to suggest and improve code. This really speeds up the process.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Caching metadata<\/h2>\n\n\n\n<p>The API provides two types of data: the actual measurements and metadata. The metadata includes location names, coordinates, units and descriptions of the measurements. When you run <code>wtr<\/code> for the first time, it will download this metadata and cache it in a SQlite database. When this database gets lost, it will simply be recreated, since it is only for caching the metadata. No measurements are stored locally.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Getting started<\/h2>\n\n\n\n<p>To list observations you need two things: the type of measure and the location.<\/p>\n\n\n\n<p>Run this command to obtain a list of available measures:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>$ <strong>wtr list -c measures<\/strong>\nCompartment_Code (Description): Measure (Description) - Note\nBS (Bodem\/Sediment): ACTVTCCTTE (Activiteitsconcentratie) - Activiteitsconcentratie americium 241 in Bodem\/Sediment t.o.v. drooggewicht in Bq\/kg\nBS (Bodem\/Sediment): D10 (maximale korreldiameter voor 10% van het monstervolume) - Maximale korreldiameter voor 10% van het monstervolume Bodem\/Sediment t.o.v. drooggewicht in um\n<em>(...)<\/em><\/code><\/pre>\n\n\n\n<p>Run this command to see all locations, including the available measure(s):<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>$ <strong>wtr list -c locations<\/strong>\nLocation (Name): Compartment_Code (Description) - Measure (Description)\nA12 (A12 platform): LT (Lucht) - WINDRTG (Windrichting)\nA12 (A12 platform): LT (Lucht) - WINDSTOOT (Maximale 3\" windstoot in de afgelopen 10 minuten)\n<em>(...)<\/em><\/code><\/pre>\n\n\n\n<p>So let&#8217;s say you want information about the flow rate in one of our rivers, dutch&nbsp;<em>stroomsnelheid<\/em>. First find the corresponding measure, for example:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>$ <strong>wtr list -c measures | grep -i stroom<\/strong>\nOW (Oppervlaktewater): STROOMRTG (Stroomrichting) - Stroomrichting Oppervlaktewater t.o.v. ware Noorden in graad\nOW (Oppervlaktewater): STROOMSHD (Stroomsnelheid) - Stroomsnelheid Oppervlaktewater m\/s<\/code><\/pre>\n\n\n\n<p>So the measure we need is named&nbsp;<code><em>STROOMSHD<\/em><\/code>. Next find locations for this measure:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>$ wtr list -c locations | grep STROOMSHD\nALBL (Alblasserdam): OW (Oppervlaktewater) - STROOMSHD (Stroomsnelheid)\nALBSDRTOVR (Alblasserdam rechteroever (nabij brug)): OW (Oppervlaktewater) - STROOMSHD (Stroomsnelheid)\nBORNDP (Borndiep): OW (Oppervlaktewater) - STROOMSHD (Stroomsnelheid)\n<em>(...)<\/em><\/code><\/pre>\n\n\n\n<p>We are interested in the location <em>ALBL<\/em>, so let&#8217;s limit the list<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>$ wtr observe -l ALBL -m STROOMSHD\n#\n# Description: Stroomsnelheid Oppervlaktewater m\/s\n# Compartment: OW (Oppervlaktewater)\n# Measure: STROOMSHD (Stroomsnelheid)\n# Location: ALBL (Alblasserdam)\n#\n# Timestamp                      Value\n  2023-12-29T06:20:00.000+01:00  -0.427\n  2023-12-29T06:30:00.000+01:00  -0.348\n  2023-12-29T06:40:00.000+01:00  -0.272\n  2023-12-29T06:50:00.000+01:00  -0.227\n  2023-12-29T07:00:00.000+01:00  -0.15\n  2023-12-29T07:10:00.000+01:00  -0.075\n  2023-12-29T07:20:00.000+01:00  0.021\n  2023-12-29T07:30:00.000+01:00  0.125\n  2023-12-29T07:40:00.000+01:00  0.218\n  2023-12-29T07:50:00.000+01:00  0.286\n  2023-12-29T08:00:00.000+01:00  0.377\n  2023-12-29T08:10:00.000+01:00  0.465\n  2023-12-29T08:20:00.000+01:00  0.525\n  2023-12-29T08:30:00.000+01:00  0.567\n  2023-12-29T08:40:00.000+01:00  0.616\n  2023-12-29T08:50:00.000+01:00  0.662\n  2023-12-29T09:00:00.000+01:00  0.717\n  2023-12-29T09:10:00.000+01:00  0.78<\/code><\/pre>\n\n\n\n<p>Default the most actual 18 rows are shown, which represents the past 3 hours (most measurements do have a 10 minute interval). If you want to go further back in time, use the&nbsp;<code>--rows<\/code>&nbsp;(or&nbsp;<code>-r<\/code>) option, for example:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>$ wtr observe -l ALBL -m STROOMSHD -r 144  # 24 hours<\/code><\/pre>\n\n\n\n<p>Notes:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Although the list of locations and measures is extensive, not all are currently in use. So for many combinations you will find no actual data. Often you get multiple locations for the same site, only one reporting the requested measure.<\/li>\n\n\n\n<li>Measure history is limited to 48 hours (288 rows for 10-minute data).<\/li>\n\n\n\n<li>Most measures are in dutch, even keys\/labels in the API data are in dutch, so if you&#8217;re not familiar with the dutch language, usage this tool and the retreived data will be limited.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Source code<\/h2>\n\n\n\n<p>As mentioned before in this article, the source code is available on GitHub:<\/p>\n\n\n\n<p><a href=\"https:\/\/github.com\/pa3hcm\/wtr\">https:\/\/github.com\/pa3hcm\/wtr<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Here in the Netherlands we have a public organisation named Rijkswaterstaat. This organization keeps track of all the water in and around our country. Think about sea levels, locks in rivers, etc. To do so they have an extensive network of sensors around the country, monitoring water levels, current speed, temperatures, etc. I wrote a [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":2127,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[10,71],"tags":[104,109],"class_list":["post-2126","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-homebrew","category-software","tag-python","tag-water"],"blocksy_meta":[],"_links":{"self":[{"href":"https:\/\/www.pa3hcm.nl\/index.php?rest_route=\/wp\/v2\/posts\/2126","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.pa3hcm.nl\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.pa3hcm.nl\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.pa3hcm.nl\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.pa3hcm.nl\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=2126"}],"version-history":[{"count":3,"href":"https:\/\/www.pa3hcm.nl\/index.php?rest_route=\/wp\/v2\/posts\/2126\/revisions"}],"predecessor-version":[{"id":2131,"href":"https:\/\/www.pa3hcm.nl\/index.php?rest_route=\/wp\/v2\/posts\/2126\/revisions\/2131"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.pa3hcm.nl\/index.php?rest_route=\/wp\/v2\/media\/2127"}],"wp:attachment":[{"href":"https:\/\/www.pa3hcm.nl\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=2126"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.pa3hcm.nl\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=2126"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.pa3hcm.nl\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=2126"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}