Read the interview with Maurice: Lead Front-end Developer ✌️

The interactive roadmap of Water4Change

Samuel Spoorenberg
Samuel Spoorenberg
Front-end Developer
Water4Change

For the research institute Drift, we worked on developing the website for Water4Change (W4C), a project that provides insights into urban water management in Indian cities.

An important goal of the website was to visually represent complex networks of processes and their interrelationships. But how do you design a system where users can add arbitrary processes and connections without having to think about the layout? That’s exactly what we set out to tackle. The result of this is the pathways component.

Why we needed a smart algorithm

The pathways component consists of cards connected by lines. Each process is a link in a larger system, and the lines represent the relationships between those links. The component needed to meet several requirements, which brought along many interesting challenges. First, the system had to be flexible enough to process arbitrary data from a CMS (Content Management System) and convert it into a dynamic visualization. Additionally, the processes needed to be positioned in such a way that lines would not overlap, ensuring the visualization remained clear, regardless of the amount of data. But how do you organize this data automatically, avoiding overlaps or visual chaos? With this question in mind, we began developing an algorithm to address these challenges.

How we built the pathways

The core of this component is an algorithm that automatically calculates the best positioning and layout. This algorithm analyzes, for example, how far apart processes should be, where lines should be drawn, and how overlaps of connections can be avoided. The calculations ensure a clear and user-friendly visualization, regardless of the complexity of the data. Below is an example of a function that calculates the vertical distances between processes:

export const getVerticalDistances = (
refs: RefItem[],
): { id: string; distances: Record<string, number> }[] => {
const verticalDistanceArray = refs.map(ref => {
const distances: Record<string, number> = {};
ref.relationIds.forEach(relationId => {
const relatedRef = refs.find(item => item.id === relationId);
if (relatedRef) {
const distance = relatedRef.rect.bottom - ref.rect.bottom;
distances[relationId] = distance;
}
});
return { id: ref.id, distances };
});
return verticalDistanceArray;
};

To determine the positions of the lines, we incorporated a series of clever techniques and functions. One example is how the algorithm calculates offsets to position the lines in such a way that they don’t cross each other. The algorithm works with variable distance calculations and counts the number of lines going to and from each card, distributing them evenly across the specified distance. Through dynamic adjustments, the presentation remains intuitive and scalable.

export const getLeftOffsets = (process: LinkedProcess, pathwayCardHeight: number): number[] => {
const offsetArray = process.relationIds.map(
(_, i) =>
-pathwayCardHeight / 2 +
(pathwayCardHeight / (process.relationIds.length + 1)) * (i + 1)
);
return offsetArray;
};

Ultimately, these calculations are passed as variables to a React component, which then generates an SVG by drawing the lines:

<svg xmlns="http://www.w3.org/2000/svg" height={verticalHeight}>
<line
x1={0}
x2={24 - horizontalOffset + strokeWidth / 2}
y1={yStart + leftOffset}
y2={yStart + leftOffset}
/>
<line
x1={24 - horizontalOffset}
x2={24 - horizontalOffset}
y1={yStart + leftOffset}
y2={yStart + difference + rightOffset + strokeWidth / 2}
/>
<line
x1={24 - horizontalOffset - strokeWidth / 2}
x2={48}
y2={yStart + difference + rightOffset}
y1={yStart + difference + rightOffset}
/>
</svg>

The result: an intuitive and dynamic component

The image below shows an example of the output generated with Pathways. Here, you can see how processes (represented as colored cards) are logically positioned, while the lines clearly indicate the relationships between these processes.

Pathways component
The pathways component

Each process is interactive, and users can click on them for more information or to discover related processes. The dynamic layout ensures that all data is displayed clearly, even with a large number of processes and relationships.

Proces details
Proces details

What can Humanoids help you with?

Want to find out what we can help you with?Interested?