Train network

G U I D E

  • Custom Visual: HTLM VizCreator Flex

    Main features used:

  • HTML VizCreator with DAX measures

  • SVG paths with CSS motion

  • % Completion metric driving animation

You can download the .pbix here

Why this use case?

Power BI native visuals do not support custom diagrams or user-defined paths. They cannot place data points on a custom layout or move them along routes that you design.

This use case tracks high speed trains along their routes.

A Table or Matrix visual can show the trains table, but a layout that mirrors the actual map is more intuitive. Seeing train positions on the route speeds up checks such as proximity between trains, potential bottlenecks, and whether a train sits between two stations and closer to one than the other.

Representing the real layout makes analysis faster and more direct. HTML VizCreator suits this scenario. It can place data on a custom map or diagram and reflect movement along defined paths.

#1 High level overview

HTML VizCreator renders HTML code that comes from a simple Power BI measure. The measure returns an HTML string, and HTML VizCreator displays that HTML inside the visual.

What is HTML? HTML is the standard language that structures content on the web. It defines elements such as headings, paragraphs, and containers that hold other visual elements.

For this report, HTML VizCreator uses HTML support for SVG elements. Many web pages use SVG images inside HTML, and the same pattern applies here.

SVG as the background map

TAV Italy(1)

What is SVG? SVG is an XML-based language for two dimensional graphics.

SVG images are defined as vectors, not pixels, so they can scale to any size without losing quality. This makes them a good choice for maps, paths, and dynamic icons.

In this report, the SVG image is the background map. It contains the train lines, the stations, and

other contextual information that should stay fixed behind the moving elements.

Dot elements as trains

test 1

The HTML code can also define elements that only exist inside that specific HTML block.
In this case, dot elements represent the trains on the map.

Each dot is an HTML <div> element. CSS properties such as offset-path and offset-distance parameterize these dots. This forces each dot to follow a specific SVG path, such as “Vector 7” for Train A, that corresponds to a real railway line (for example, from Turin to Venice).

Each dot is tied to the % Completion metric in Power BI.
% Completion is a metric that returns a value from 0 to 100 percent and represents how far the train has progressed toward its final destination.

Screenshot 2025-11-13 141342 1

#2 Understanding the data

The dataset contains a snapshot of the entire train network every 10 minutes. Each timestamp records all trains that are active at that time.

the data1

From the sample table:

  • At 06:00:00 there is a single train in the network: Train A. It is departing on the red line with 0% Completion.
  • At 06:50:00 there are two trains active: Train A and Train E.

Filtering the Hour column in Power BI returns the state of the network at that specific time.
Each hour value behaves like a time slice of the system.

% Completion and positioning on the SVG map

The % Completion column represents the progress of a train toward its final station.
Values go from 0 to 100 and describe how far the train has moved along its route.
Percentage was chosen instead of distance in kilometers because it simplifies the HTML parametrization:

  • The SVG path defines the route between the departure and arrival stations.
  • The dot element in the HTML moves between the start and end of that SVG path.
  • A value of 0% places the dot at the departure station, 100% at the final station, and any value in between maps to an intermediate position.

This numeric mapping is what drives the dot position inside HTML VizCreator.

Trains, granularity, and consistent timestamps

The sample dataset includes only five trains: A, B, C, D, and E. However, the structure is not limited to five; more trains can be added.
The main constraint in this example is data granularity:

  • Observations are recorded every 10 minutes, starting from 06:00.
  • At each timestamp, all active trains share the same datetime value.

Other granularities are possible. A one minute or five minute interval would also work. The important rule is that trains must share consistent timestamps.
Mixed timestamps such as:

  • Train X at 08:00:00
  • Train Y at 08:03:27

would create confusing report behavior.
If a user filters the report by 08:03:27, only Train Y would appear. This could suggest that only one train was running at that time, even if other trains were active but recorded at 08:00:00.

Power BI Train with table

To avoid this, keep all train observations aligned on the same time grid.

#3 Creating the measure

The measure used by HTML VizCreator must return HTML code as text.
The result of the measure is a single text value, wrapped in double quotes, that contains the full HTML string.
In this example measure, the HTML string includes:

  • The SVG image used as the background map
  • The dot elements that represent the trains moving along the SVG paths

Using variables to position trains

Inside the measure, each train has a variable that controls where its dot appears.
In the HTML code, the position attributes of each dot (each train) are replaced with a variable that depends on the current % Completion value in the filter context.

veriable-position

When the report is filtered by time or by train:

  • The filter context changes the value of % Completion for each train.
  • The measure recalculates.
  • The variables update the HTML, which moves the dots along their SVG paths.

In this showcase, there are five trains, so the measure defines five variables and five dot elements.The number of trains is fixed.
You can extend the pattern:

  • Manually increase the number of variables and dot elements to support more trains, up to several hundred or even 1000, by repeating the same structure. AI tools can help generate the repeated sections once the base pattern is ready.
  • For a more scalable approach, consider using DAX to build an on-the-fly table inside the measure and then generate the HTML from that table. This allows for a dynamic number of trains but introduces more complexity and may impact performance.

Making labels move with the trains

The measure also defines labels that move with each dot along the route.
The label position uses the same logic as the dot:

  • The SVG path defines the train route.
  • The % Completion value determines the point along that path.
  • Both the dot and its label use that value to stay aligned on the map.
    In this use case, the label text (for example “Train A”, “Train B”) is hard coded in the HTML string.
This can also be made dynamic by using a DAX expression that retrieves the train name from the model and injects it into the HTML string in place of the hard coded text.

Making labels move with the trains

Finally, a measure must return the HTML text; replace internal double quotes with single, and wrap overall string and concatenations in double quotes.

From double quotes to single quotes

#4 SVG and parametrization

The high speed train network for Italy was drawn manually in Figma, a free design tool.
The goal was to export the layout as an SVG file that could drive the animation in HTML VizCreator.
Each line, station, and shape in the drawing is a separate object.
When Figma exports the file as SVG, every object becomes an element in the XML code with its own id attribute.
The SVG file is plain XML. Everything sits between the opening and closing tags:

parametrization

Inside that code, each route segment is a path element.
For example, Vector 7 is the path that defines one specific train route. This is the path that the dot and the label will follow later in the HTML.

After finishing all background elements in Figma, the workflow is:

  1. Export the drawing as SVG.
  2. Open the SVG file and copy the XML content.
  3. Embed that XML block inside the HTML code.

This turns the static Figma drawing into an SVG map that HTML and CSS can animate.

Linking SVG paths to moving dots

In the HTML, the CSS class for each dot uses the SVG path as its route.
 The key properties are offset-path and offset-distance.

  • offset-path tells the browser which SVG path to follow.
  • offset-distance tells the browser how far along that path to place the element.

Below is the CSS for two dots:

.dot{
offset-path: url(#Vector\ 7);
width:14px; height:14px; border-radius:50%;
offset-distance: var(--PERCENT);
background:#111;
outline:3px solid #fff;
}
.dot2 {
offset-path: url(#Vector\ 8);
width:14px; height:14px; border-radius:50%;
offset-distance: var(--PERCENT2);
background: #111;
outline: 3px solid #fff;
}

A few points to note:

  • url(#Vector\ 7) and url(#Vector\ 8) reference the SVG paths with IDs Vector 7 and Vector 8.
  • The backslash escapes the space in the ID so the CSS selector stays valid.
  • offset-distance uses CSS variables (--PERCENT, --PERCENT2) that represent the current position of each train along its route.

Those CSS variables are set in the HTML generated by the DAX measure, based on the % Completion value in the current filter context.
 So when % Completion changes, offset-distance changes, and the dots move along Vector 7 and Vector 8 on the SVG map.