Vector Map Creation for Stadium Bukit Jalil, Malaysia
Atif Aiman
Posted on December 4, 2023
Salam and holla guys!
Before I blabber myself talking about the process of me creating the vector maps, I would like to give you some background about a project that I have contributed, called BukitJalilStadium, which kickstarted by a great guy, @afrieirham, and you should check out his profile and his project!
In case you are curious, you can check out the repository of Bukit Jalil Stadium and the deployed version: bukitjalilstadium.com.
In case you don't know where Bukit Jalil Stadium located, it is one of the main stadium of Malaysia, which is located at Kuala Lumpur, Malaysia. You might want to pay a visit and Stadium Bukit Jalil can be one of the spot you want to look out for!
Well, let's get started!
Inspecting the original BukitJalilStadium.com
Afrie has created this website, because he is inspired from a project by Peter Askew, person behind VaughtHemingway. And overnight, the project created by Afrie suddenly become a Malaysia's sensation.
As a normal, curious person, I just look into the website, and it is cool, considering the high-definition photo he took previously, and well-organised photo. However, there is something that I feel kind of cumbersome - I need to keep crosschecking between the link and the stadium layout he provided before. Mind that, within a short time, he actually done a really great job, but I really wish we can improve the experience of navigating each seatings in the stadium.
If you noticed, the top section is a list of seat codes (which are the links), and the image of stadium layout. I feel like it is tiring to keep seeing which seating that I want to survey from.
And then, the eureka idea comes in! What if we can interact with the stadium layout, and just navigate from the layout?
Utilising the AI to Learn
Honestly, even though I wish I can do something, I don't know how to do it. So, since now we have second big brain, why not just try asking AI to find a possible solution? That is when I open ChatGPT 3.5 to prompt how can I make the stadium layout interactive.
You can follow my prompts with ChatGPT on how can I implement stadium layout interactions, as we did with maps.
Straight away, ChatGPT suggests SVG map. If you follow my prompts, I even ask for silly improvements, including rotations, non-rectangular seating shapes, and so on. Alas, I got the idea on how to do it. I tried to run the suggestions given by ChatGPT, and well, I am pretty satisfied. But, there are some challenges that I faced, right before the development.
Problem Identification
Even though I already got some idea, there are several things that I need to consider:
- I have no SVG whatsoever for the stadium layout. Though it is elliptical, I still need to map the seatings to each sectors.
- I need to think about ease of access, so that user can choose between interacting with vector maps, or just with the links that we had before.
- I need to think about scalability - what can we do next with this vector maps, so I can put that in mind when doing the vector maps interaction.
Well, yes, I am a bit overthinking kind of guy, but hey, this is a new problem for me, and I would love to see how this will turn out.
Let's start with the first problem, shall we?
SVG Map Creation
I was from the graphic design background. I learn to utilise whatever tools that I learn to used before, for all my contents, including this article's header. I craft all my assets on my own.
And there is where it hit the nails. Why don't I just create my own SVG in Figma?
What I did, is I take stadium layout provided by the original website, and take it into my Figma. And all the tracing job begins. And all of that, is a manual job.
And so, this is the comparison after I traced all the layouts with the photo, side by side.
The thing that I love about Figma, is I can just use pen tool, and create whatever vector stock that I need, and change it to something else, without having to rely on designers to do it for me. You might ask - "why don't you just use the tool to trace it instead?". Well, I am a perfectionist, and I feel fragile after testing some of them, and not getting what I want 😅, so, manual job, here I go!
So, I have traced every sectors in the stadium, that is nice! But does the challenge stops there?
Bringing the Vectors to Code
To preface the next challenge, here is the output, after I exported it from Figma as SVG. (I will just put few example, because the output is so damn long!)
<svg width="2139" height="1718" viewBox="0 0 2139 1718" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M531 1577.5L517.5 1613.5L661.5 1662.5L674 1626.5L531 1577.5Z" fill="white" stroke="black"/>
<path d="M531 1577.5L674 1626.5L702 1531L655 1517L646.5 1539.5L611.5 1527.5L619 1506L573 1485.5L531 1577.5Z" fill="white" stroke="black"/>
// The rest of SVG
</svg>
There are another set of problems coming in:
- It is hard to store each paths, because if I want to change something, or at least include something, I need to go through all paths (for Stadium Bukit Jalil, we have a total of 133 sectors, including roofs). So, I need a simpler way to store these info, without jeopardising the assets).
- The path of each sectors are not arranged as I want, it is damn messy. But the sectors are shaped as I traced in Figma.
- There are position constraint and preset fill and stroke. I need to make is customisable.
- I have to make every paths interactive, so every paths should be wrapped with something.
So, let's go through each of these problem.
Problem #1: Path Storage
I need to store all these paths inside SVG properly, so that I can reuse whatever stuff that is generated and modify it at will. Assume that we can just throw all these information to the database, and we can disable from there (like, what if the stadium manager want to disable one sector for maintenance purpose).
To do that, I have to split all the props from the paths. Do you think I want to do another layer of manual jobs? Nah, we can do it the "React way".
So, for temporary, I did this code.
import React, { useEffect, useRef } from 'react';
const SvgComponent = () => {
const svgRef = useRef(null);
useEffect(() => {
// Function to extract all attributes from a path element
const extractPathAttributes = (pathElement) => {
const attributes = {};
// Get all attributes of the path element
for (let i = 0; i < pathElement.attributes.length; i++) {
const attribute = pathElement.attributes[i];
attributes[attribute.name] = attribute.value;
}
return attributes;
};
// Reference to the SVG element using useRef
const svgElement = svgRef.current;
if (svgElement) {
// Get all path elements in the SVG
const paths = svgElement.querySelectorAll('path');
// Extract attributes from each path
const pathAttributesArray = Array.from(paths).map(extractPathAttributes);
// Console it, so I can copy later on
console.log(pathAttributesArray);
}
}, [svgRef]);
return (
<div>
<svg ref={svgRef} width="100" height="100">
<path d="M10 10 L90 10 L50 90 Z" fill="red" stroke="blue" />
</svg>
</div>
);
};
export default SvgComponent;
And, this is the sample output in the console:
[
{
fill: "white",
stroke: "black",
d: "M58.5 639.5L22.5 628.5L1.5 696L38 707.5L58.5 639.5Z"
},
{
fill: "white",
stroke: "black",
d: "M109.5 445.5L143.5 462.5L62.5 628L28.5 610.5L109.5 445.5Z"
},
{
fill: "white",
stroke: "black",
d: "M147.5 452.5L120 427L221 306L250 330.5L147.5 452.5Z"
}
]
What I did, I just copy whatever is in the console, and paste it into a file to store all these info, into sectors.ts
. This will serve as configurations, and I can scale it to whatever I feel I need to add, which I will describe later on.
Well, now, I have list of arrays of sectors' configuration. That is nice. Let's go to the next problem.
Problem #2: Path Arrangement
We have all sectors' information, but there is one thing - THE SECTOR PATH ARRANGEMENTS ARE EVERYWHERE!
I went through yet another manual job - rearranging all paths. Since I need to identify all paths, I add another attribute to each path, id
.
{
id: "312",
fill: "white",
stroke: "black",
d: "M147.5 452.5L120 427L221 306L250 330.5L147.5 452.5Z"
}
If you refer to the original photo, each sectors has its own "code", thus I use it as ID. This is important, since I need to identify which sector I am modifying, rendering (React requires key identification), and well, readability when I go through each items for further modification.
And well, I am the perfectionist. Guess what I did to these arrays of path information? Yeah, I rearrange every sectors arrangement! Takes nearly 1 hour to do so 😭.
Okay, cool! Now we solve 2 problems - storage and arrangement. Onto the next!
Problem #3: Path customisations
This is not really a big problem, because we have all paths processed and arranged according to my vision. So now, we need to jump into React code and start mapping all these info.
<svg width="2139" height="1850" viewBox="0 0 2139 1750" className="w-[800px] h-auto">
{
sectors.map(({id, ...sectorProps}) => (
<path
key={id}
className="hover:fill-[#444]"
{...sectorProps}
/>
))
}
</svg>
Since now, we know that all of the elements inside <svg>
we have are all <path>
, we can just loop into sectors, and pass all the props to the <path>
element.
And voila! We have our customisable paths, so we can remove, add, or do something on top of them. For example, interactions! Just a simple one, I make it so that it will change the fill once it is hovered. If you want to make the sectors to have its own category, you can actually change fills and strokes inside sectors
, and it will automatically change the paths.
Now, for the last problem.
Problem #4: Interactions
Well, in HTML, we can have different ways for interaction. We can have buttons, anchors (links or <a>
, if you are not aware), and others. But, I need to make it accessible as well, so that people with challenges, such as people without mouse (I am looking at you guys, Vim users!) can benefit from this feature!
I can use button, however inside SVG, button behaves differently, so button have to go out of the question. Now, leave me using anchors. The benefit of us using map from the array to the <path>
, is that, we can just wrap the <path>
that we have with <a>
without so much effort. React rocks!
<svg width="2139" height="1850" viewBox="0 0 2139 1750" className="w-[800px] h-auto">
{
sectors.map(({id, ...sectorProps}) => (
<a id={id} key={id} href={LINK_TO_PAGE}>
<path
key={id}
className="hover:fill-[#444]"
{...sectorProps}
/>
</a>
))
}
</svg>
So now, you have vectors which are interactive! Of course, you can change it anyway you want, and ready for future improvement, such as search function!
The Result
And so, I sent the video to him, and he decided to post it.
Of course, there are still a lot of stuff happening here, but I will keep this article focused on the vector map interactivity. From here point onwards, it is up to you to add more creativity. As you see from the X post, I also added other interactions, such as displaying ID as I hover through sections, added some texts, active states and so on. But once you get the basics of how we add the vectors, you just have to add more on top.
Conclusion
Well, that is how you want to make a simple interactive vector maps! Since I have this perfect use case to do it, so I just go crazy over it, and make one myself. At least, now I understand how people do vector maps, and do so many things with it. But, you also need to have the ability to make vectors yourself, and Figma being free (of course limited) is a tool to create a lot of stuff!
Just simple steps - create assets in Figma, extract paths for scalability, and just wrap all those paths in anchor for interactivity (and accessibility on top).
It doesn't have to be <path>
, it can be square at first, or ellipsis. Once you grab the fundamentals, you can make your own paths in Figma, and go wild with it.
And that's it, folks! A long article, but I hope this will be beneficial for you guys. Until next time, goodbye, and peace be upon ya!
Posted on December 4, 2023
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.
Related
November 9, 2022