Open-source platform for opportunistic cetacean observation: a mobile app for on-board recording, a web frontend for review & analysis, and a backend built around the scientific workflow of M.E.E.R. e.V. off La Gomera.
User manual: EN · DE · ES | Data guide: EN · DE · ES | Wiki · API docs
- What MWPA does
- Quick start
- Architecture
- Features
- Tech stack
- Data & scientific use
- Documentation
- Project status
- Contributing & community
- Citing MWPA
- Supervisors
- License
Whale- and dolphin-watching tours generate huge amounts of valuable observation data. Most of it used to disappear in paper logbooks or a sprawling Access database. MWPA brings that data into one place so it can be reviewed, exported, and analysed.
The platform sits on top of the long-running research programme of M.E.E.R. e.V. (Marine Education, Environment, Research e.V.) for cetacean monitoring off La Gomera (Canary Islands, Spain).
Three audiences, one dataset:
- Observers on board record sightings (species, group size, behaviour, photos, GPS track) on the mobile app — even without network.
- Reviewers in the office verify, correct and complete records via the web frontend.
- Researchers / authorities export curated subsets for analysis or as regulatory deliverables (e.g. AROC).
Prerequisites: Node.js 18+, npm, Docker (for MariaDB).
# 1. Clone with submodules (the in-house bambooo widget library is one)
git clone --recurse-submodules https://github.com/stefanwerfling/mwpa.git
cd mwpa
# 2. Bring up the database
docker compose up -d mariadb
# 3. Install everything (npm workspaces)
npm install
# 4. Build: schemas -> backend -> frontend
npm run compile
# 5. Start the backend (binds https://localhost:3000 with a self-signed cert)
node backend/dist/main.js --config=config.dev.jsonOpen the web app at https://localhost:3000 (accept the self-signed certificate). The mobile app lives in a separate repository.
For deployment, configuration, the IM2020 legacy importer and the AROC report workflow see the project wiki.
┌──────────────────────┐ sync ┌──────────────────────┐
│ Mobile App (Flutter)│ ─────────► │ │
│ offline-first │ │ │
└──────────────────────┘ │ Express + figtree │
│ (TypeScript) │
┌──────────────────────┐ REST │ │
│ Web Frontend (SPA) │ ◄────────► │ /json/* /mobile/* │
│ AdminLTE + bambooo │ │ │
└──────────────────────┘ └──────────┬───────────┘
│ TypeORM
▼
┌──────────────────────┐
│ MariaDB │
│ observations, tracks│
│ derived analytics │
└──────────────────────┘
▲
│ enrich
┌──────────┴───────────┐
│ Background services │
│ depth · weather · │
│ movement analytics │
└──────────────────────┘
The repo is an npm workspaces monorepo with three packages that build in order:
| Package | Role |
|---|---|
schemas/ |
Runtime types & validators (using vts), generated from schemas/schemas.json via vtseditor. Shared by backend & frontend. |
backend/ |
Node.js Express app on the figtree framework — services, repositories, REST routes. |
frontend/ |
Browser SPA — webpack bundle, AdminLTE 3 + jQuery + Bootstrap 4 + the in-house bambooo widget library. |
- Offline-first sighting recording — species, group size, behaviour, photos
- Continuous GPS tracking during the tour
- Background sync once a network is available
- Sighting list with multi-column sort, in-header search, infinite scroll
- Map view with sighting markers + computed movement tracks (toggleable layer)
- Dashboard with crossfilter (date, species, organisation, vehicle, driver)
- Per-sighting analytics, year-on-year comparison, behavioural summaries
- Edit modal with photo gallery and validation against the shared schema
- Users, groups & roles (RBAC via
rbac-simple) - Vehicles, drivers, organisations, species & species groups
- Encounter categories, behavioural states, external receivers (AROC)
- Orphan tracks admin page — buckets of pending GPS points whose parent tour never landed (typical when crew/boat is corrected after the fact). Includes a map preview of the actual points so the admin can decide assign-vs-delete before re-pointing them at a different
SightingTour. - Bulk re-derivation of analytics (e.g. movement tracks, sighting × earthquake correlations)
- Excel export with column picker & coordinate-format selector (decimal / DMS / DM / all) — includes an Info sheet documenting the active filter
- AROC office-report export (filled
PLANTILLA_AVISTAMIENTOS_AROC.xlsx, one file per boat per half-year) - Printable Data Page (Map + Analytics + Year comparison) via the browser's PDF export
- Derived movement tracks per sighting: distance, speed, heading, turning-angle, with GPS-jump flag
- Per-sighting environmental enrichment — bathymetry, SST, chlorophyll-a, sea-surface salinity, sea-level anomaly, UV index (Open-Meteo + ERDDAP + NASA POWER), fishing effort (Global Fishing Watch)
- Seismic correlation — hourly cron pulls earthquakes from USGS + EMSC (FDSNWS) for every org's tracking area, joins to nearby sightings within ±14 days / 200 km, exposes them on the per-species profile and an interactive impact-analysis page (pick a window — ±24 h / 3 d / 7 d / 14 d — see affected sightings, their movement tracks, and analytics buckets by species / behaviour / encounter / hours-offset)
- Cross-species regression matrix with Simpson's-paradox visibility (per-species + pooled regression lines on the same plot)
- Effort-only tours — legs without sightings are still preserved server-side from the uploaded GPS track, so they appear in sighting-rate and survey-effort analyses
| Layer | Technology |
|---|---|
| Language | TypeScript (NodeNext ESM in backend, webpack bundle in frontend) |
| Backend framework | figtree (Express + service-manager + DBRepository + RouteLoader) |
| ORM | TypeORM on MariaDB |
| Schema validation | vts generated from JSON via vtseditor |
| Auth | Express session + custom RBAC (mwpa_schemas rights) |
| Frontend UI | AdminLTE 3, jQuery, Bootstrap 4, bambooo widgets |
| Maps | OpenLayers + ol-layerswitcher, OSM tile cache |
| Analytics | d3 + dc.js (crossfilter), in-page charts and dashboards |
| Office exports | node-xlsx (sightings list), JSZip direct-XML patch (AROC template) |
| External data | Open-Meteo + NASA POWER (weather/UV), ERDDAP (SST/chl-a/SSS/SLA/currents), Global Fishing Watch 4Wings (fishing effort), USGS + EMSC FDSNWS (earthquakes), AISStream.io (live vessel positions) |
| Mobile | Flutter (separate repo) |
| Local infra | Docker Compose (MariaDB) |
A separate guide describes how data is collected, what every field means, how the derived movement tracks are computed, and how to use the dataset for scientific analysis — including a reproducibility checklist and an honest list of limitations.
- English — Data collection & scientific use
- Deutsch — Datenerhebung & wissenschaftliche Nutzung
- Español — Recogida de datos y uso científico
Topics covered:
- Data sources (mobile app, web frontend, importers, background services)
- Domain model — tour ▸ sighting ▸ tracking ▸ extended ▸ movement
- Field-by-field reference
- Coordinate & time conventions (WGS84 / UTC / local-time caveats)
- Derived movement tracks & quality flags
- Recommended analyses & their limitations
- Reproducibility checklist
- Citation guidance
| Resource | What's inside |
|---|---|
| User manual — EN · DE · ES | Page-by-page walkthrough of the web frontend with screenshots |
| Data & scientific use — EN · DE · ES | Field reference, derived analytics, reproducibility checklist |
| Project wiki | Installation, deployment, IM2020 importer, AROC workflow |
| REST API | Full endpoint reference (Stoplight) |
| Database schema | ER diagram of all entities |
| CLAUDE.md | Developer onboarding (used by Claude Code & humans) |
- Active — under continuous development. See issues for the current backlog.
- Backend migration — the backend is being ported from
old-backend/(legacy routing-controllers) tobackend/(figtree framework). Most main and mobile resources are ported; seeold-backend/only for unported logic that needs to move over. - Test suite — not yet. Contributions welcome.
The mobile app (Flutter) is in production with M.E.E.R. e.V., so any change touching /mobile/* REST endpoints must keep the wire format backward-compatible.
You're welcome to:
- File issues / feature requests on GitHub
- Send pull requests — please follow the existing ESLint rules (4 spaces, single quotes, strict TS) and the CLAUDE.md conventions for schemas, routes and migrations
- Chat on Gitter
- Support the underlying NGO at M.E.E.R. e.V. (donations, membership, on-site volunteering)
- Sponsor development: Buy me a coffee · Liberapay
|
Scientific partner |
"Working with the people was wonderful and important." MWPA exists because of the volunteers of M.E.E.R. e.V., who collect this data under real-world conditions tour after tour. Please support their work. |
If you publish work based on data from this system, please:
- Acknowledge M.E.E.R. e.V. as the data provider — m-e-e-r.de.
- Cite the collection period (start / end date of the included sightings), not the tool version.
- Contact M.E.E.R. e.V. for a project-specific data-use agreement before publication.
See the data & scientific use guide for the recommended attribution wording and the reproducibility checklist.
- Christina Sommer — M.E.E.R. e.V.
- Stefan Werfling — Pegenau GmbH & Co. KG
This project is licensed under the GNU General Public License v3.0. See the LICENSE file for the full text.

