Skip to main content
Back to list

Projects Map

Interactive project map for the Interreg Baltic Sea Region

Year: 2021 JavaScriptLeafletPHP

Built during my internship at @iService: an interactive map that visualises project partnerships for the EU programme Interreg Baltic Sea Region. The map shows who works with whom on which programme — across borders, with hundreds of partners often at the same physical address.

The problem

Interreg funds cross-border projects. A researcher, NGO, or city looking for a project partner wants to see who has already collaborated on similar topics, in similar regions, in what constellations. The data exists in a WordPress database, but without spatial context it’s hard to navigate. Goal: a map on which every partnership is a line between lead partner and project partner, with filters by programme, region, and project.

Constraints

Internship project with a defined scope, embedded in an existing WordPress site. Data source was a custom-post-type setup in WordPress I couldn’t change. Performance target: render hundreds of markers and thousands of lines simultaneously, smooth even on older laptops. Visual target: don’t fight the host site’s branding, and offer a dark mode for long sessions.

Architecture

Frontend is vanilla JavaScript with Leaflet as the map library. For the flow lines between lead and project partners I use the Canvas Flowmap Layer Extension — it draws onto a canvas layer instead of SVG, which is orders of magnitude faster with many connections.

On the server, a PHP layer caches project data from WordPress custom post types into a single JSON response that the frontend fetches once on load. The map doesn’t hit WordPress on every filter change — filters run entirely client-side. Filter criteria are programme, project type, and region; clicking a marker opens a popup with partner details.

Decisions — and what I rejected

Line rendering: SVG → Canvas. My first attempt drew each connection as an SVG path. At ~3000 lines the map dropped below 10 fps. Switching to the canvas flowmap layer brought performance back, because the browser no longer had to manage each path as its own DOM node. Trade-off: canvas paths aren’t directly clickable — click handling had to go through the markers, not the lines.

Cluster: standard clustering → circular spread per address. Many lead partners sit at the same university address. Standard marker clusters would have collapsed 15 partners at one location into a single cluster — defeating the map’s purpose. I wrote a function that distributes markers with identical coordinates in a small circle around the original point; the radius grows with the count. Clustering only kicks in for genuine spatial neighbourhood, not for coincident points.

Data layer: direct WordPress calls → PHP caching layer. Live calls would have been ideal for freshness but cost too much latency. The PHP cache syncs via cron every few hours — plenty for project data that changes monthly.

Theme: custom CSS → Leaflet tile switch. Instead of writing custom map renderers for light/dark, the tool switches between two tile sources (a lighter and a darker basemap style). Small change, big effect — the map’s other parts (markers, lines, popups) are already styled via CSS.

Performance

On first load project data arrives as a batched JSON blob (one request against the PHP cache layer). Filter changes are pure client operations with no network round-trip. The canvas flowmaps hold 60 fps even while zooming because per-frame re-rendering goes through canvas operations rather than DOM mutations.

What I would do differently

TypeScript. The project was vanilla JS, which was a defensible choice in 2021. Today I’d build the same stack in TypeScript — the central data structure (partnership lines with metadata) is exactly where a type system pays back in iteration speed.

Componentisation. Filter controls, popups, and legend live as individual DOM functions in the same script. A small component structure — even without a framework — would simplify maintenance when someone wants to add filter categories two years out.

Accessibility for the map itself. Maps are a known a11y pitfall — the central visual information is nearly opaque to screen readers. A fallback list view of the same data would have made the tool reachable for more users.