An interactive web map showing the migration paths of some GPS-tracked birds between Northern/Eastern Europe and Africa.
Demo: https://bbecquet.github.io/bird-tracking/
Here is a blog post describing why and how I created this project.
Requirement: Node 18+.
npm installthen, to start a local dev server instance:
npm run devTo build a standalone version:
npm run buildIt will create a build directory with all files ready to be hosted anywhere. As everything happens on the client, it doesn't require any app server, just a plain old HTTP server.
birds.geojson) is very large (~15 Mb) and needs to be downloaded by the client when the app starts. This means this server-less architecture will not scale well if you want to work with much larger data sets.
Technically, this app is mostly a Deck.gl view, using a MapLibre GL JS instance as mapping library, itself using a freely available basemap by CARTO. Everything is wrapped in a React web app, as it's a natural environment for Deck.gl and it simplifies the management of UI.
The hard part (drawing and animating so many things) is handled by Deck.gl's TripsLayer. Basically, it's a manager for lines with timestamped coordinates. You give it all the coordinates and times, a current timestamp that you update periodically, and voilà!
Source data is coming from the "GPS tracking of Storks, Cranes and birds of prey, breeding in Northern and Eastern Europe" dataset from the Global Biodiversity Information Facility.
After some manual clean-up and simplification in QGIS, I use a Python script to heavily aggregate and transform raw CSV data into a GeoJSON FeatureCollection that is more fitted for JS manipulation. This script is in the data directory, as well as the latest CSV data I used. It takes the CSV file path as argument and prints the resulting GeoJSON on standard output.
So, to overwrite the current GeoJSON file (public/birds.geosjon), from the root of the probject:
python data/transform_birds.py data/clean_bird_migration.csv > public/birds.geojsonIf you want to adapt the app to data formatted differently, you probably won't need this script. You just have to ensure the final GeoJSON format is right. Each GeoJSON Feature in the FeatureCollection should be:
{
"id": <number>, // id of a single bird
"type": "Feature",
"geometry": {
"type": "LineString",
"coordinates": <number[][]> // lng/lat pairs for each coordinate of the path of the bird
},
"properties": {
"times": <number[]>, // timestamps for each coordinate
"species": <string> // the bird species, used to split birds into groups with different colors
}
}
In doubt, look at the content of birds.geojson, it's pretty self-explanatory.
MIT.