A monitoring platform built for the Croatian Hydrographic Institute that collects, manages and publishes oceanographic and meteorological data from a network of stations along the Adriatic coast. It ingests raw instrument files, stores measurements per station, drives a threshold alarm system, and serves live-updating station pages and a JSON / GeoJSON map API. Work done for the Institute — the platform and its data are the Institute's.
Adriatic Sea is a platform I built for the Croatian Hydrographic Institute to run its network of coastal monitoring stations. It is a Yii 2 advanced-template application: an authenticated admin backend where staff manage stations, upload instrument data and configure alarms, a public portal that shows designated stations to anyone, and a JSON / GeoJSON API that feeds a live map of the Adriatic coast.
The data model is shaped by the instruments. Five station types — tide, wave, wind, pressure and meteorological — each write into their own per-station database table, so wildly different measurements coexist without a sprawling shared schema. Tide readings are calibrated against managed mareo zero reference levels before they are shown, a threshold alarm system flags out-of-range values with colour coding and email alerts, and the whole interface runs bilingually in Croatian and English.
The public station pages stay current through interval polling rather than server push: a 30-second countdown drives an incremental fetch that asks the server only for measurements newer than the last point already on the chart, and a freshness indicator warns when a station’s latest reading is more than fifteen minutes old. It’s the right amount of machinery for data that arrives on a steady cadence.
This is work done for the Institute, not a personal project — the platform, the station network and the data belong to the Croatian Hydrographic Institute. The counts and capabilities above are taken directly from the source, and where the codebase carries a dependency it does not actually use, it has been left out rather than claimed.
Each station uploads measurement files (CSV / DAT) which are parsed and written into a database table named for that station and type. A tide station writes to its own tide table, a wave station to its own wave table, so heterogeneous instruments coexist without one giant shared schema.
A frontend API endpoint walks every station flagged for export, pulls its latest measurement with raw SQL tuned per instrument type, and returns it as JSON or as a GeoJSON FeatureCollection ready to drop onto a Leaflet-style map.
A station's page doesn't sit still: a 30-second countdown drives a polling loop that asks the server only for measurements newer than the last point already plotted, appends them to the chart, and updates the latest-reading and server-time readouts. It's deliberate interval polling, not server push — simple, robust, and right-sized for the data's cadence.
Operators define named alarms per station and measured field — a threshold value, a colour, and whether it is a minimum or a maximum. When a station's readings cross the line, the alarm surfaces visually and notifications go out by email.
Tide readings are meaningless without a datum. The platform manages mareo zero reference levels per station, so a raw sensor value is calibrated to a real, comparable tide level before it is shown or exported.
Behind login, a role system ties users to the stations they are entitled to via contracts, with actions written to an audit log. In front of it, a public portal exposes only the stations marked public — read-only, no account required.
Historical measurements can be filtered by date range and exported to delimited text for downstream scientific use, and the whole interface runs in Croatian and English from a single i18n source.
Tide, wave, wind, pressure and meteorological instruments emit completely different measurements. A single shared measurements table would be a sparse, type-checked mess, and every query would have to branch on station type anyway.
Each station writes to its own table, named from its designation and type (e.g. {code}_tide_data). The Stations model resolves the right table name on demand, so the rest of the app treats a station as a typed source and reads exactly the columns that instrument produces.
The coastal map needs the latest reading per station, formatted and meaningful, but the readings live in many type-specific tables and tide values are useless without their datum.
A single API endpoint iterates exportable stations, runs per-type SQL for the latest measurement, and — for tide — calibrates against the station's mareo zero and computes the next predicted extrema. The result is emitted as JSON or a GeoJSON FeatureCollection the map can render directly.
Scientists at the Institute need full administrative control and entitlement boundaries; the public needs a safe, read-only window — without standing up a second application or duplicating data.
Yii 2's advanced template splits an authenticated backend from a public frontend over shared common models. Contract-based access and audit logging fence the admin side, while the public portal and map API expose only stations explicitly flagged public — same data, two doors.