Leaflet for Fantasy Maps
Written: December 22, 2023
|~1345 words, 7 min read
|Preamble π
We've all seen it: When you buy a new book from any fantasy series -be it The Lord of the Rings or The Witcher -, it oftens includes a detailed map to allow you to immerse yourself in the world and its locations.
As it was expected, with the advent of the Internet and new ways to share stories, fantasy maps have evolved to adapt themselves to the new ecosystem. No need to go further than Netflix's The Witcher, where they took Andrzej Sapkowski's map of the Continent and gave it a new layer of interactivity.
Aside from the use for aesthetic utility, there's also a practicality to the concept of a fantasy map. If there wasn't, there wouldn't be a whole website dedicated to interactive videogame maps. Whatever the reason, a book series, videogame or TTRPG campaign, it's not uncommon to want to make your own fantasy map and share it online.
Leaflet.js π
As it is with most technology dilemmas, there's not a single solution to this problem. There's tens of JavaScript libraries that allow to make one's own map, and Leaflet is one of them.
Leaflet is used for typical use cases for maps in the world of web development, but if you look deeper into its documentation, you may realize that nothing's stopping you from using different geography from real-world's Earth. That's why Leaflet is an excellent option to include interactivity to your fantasy worlds.
How to make the map π
Before you can add interactivity with Leaflet and publish it to your website, you need to generate your map. There's tons of options available for your: you may choose to pick up pen and paper, paint it digitally in your software of choice, ...
However, if all other choise is out of your reach, because you lack the skill or time to make them from scratch, there's some alternatives I can recommend. You can't bring up interactive fantasy maps without mentioning Azgaar's Fantasy Map Generator. The raw amount of choice of configuration and customization it offers makes it explaining the software way out of this article's scope, but I can send you to its subreddit where you may find information and ask any questions.
Otherwise, if you are interested in smaller-scale maps, Watabou has a few generators that can generates anything from cities to kingdoms and dungeons.
Implementation π
Note: If you have no previous knowledge of Leaflet, I recommend you go through their quick start guide. It will get you up to speed about the basics of Leaflet.
Once you have the image that represents your map, you may start your implementation with Leaflet. Leaflet is very intuitive and I urge you to play around with any configuration, but let's start with some examples.
let map = L.map("map", {
crs: L.CRS.Simple,
maxBounds: bounds,
minZoom: -2,
maxZoom: -1,
maxBoundsViscosity: 1,
});
map.fitBounds(bounds);
We initialize our map with a few parameters. The parameters minZoom and maxZoom are quite self-explanatory: they allow you to know how much you can zoom in or out of the map. Depending on the image size and map you have chosen, you may need to modify these values.
The maxBounds parameter is an imaginary rectangle of where the user can move when navigating your map. In the case of this example, it would be something like this: bounds = [[0, 0], [3736, 5952]],
.
Finally, maxBoundsViscosity
is a parameter that you can optionally modify. By default, it has a value of 0.0
, and what it does is that if you go beyond the maxBounds, instead of stopping completely, it allows you to move a little (determined by the value you set) and when you release the mouse, it returns you to your position. With a value of 1
, it doesn't allow you to see anything beyond the bounds.
L.imageOverlay("assets/images/map.jpg", bounds).addTo(map);
ImageOverlay is a type of Raster Layer that Leaflet uses to render a raster image. If we were to make a real-world map, we would use GeoJSON, but in our case a static image is more than enough.
Once we've included these lines of code, our map is already displayed on the screen. At this point, we could consider the implementation complete and publish it directly on our website. However, there are still some things we can do to improve its interactivity.
If you have any implementation questions, here's the code we've written up to this point.
See the Pen Leaflet JS 1 by Gary CuΓ©tara (@KuluGary) on CodePen.
Markers π
It's not uncommon for an interactive map to have markers that specify the position of certain points of interest in our fantasy world. If we take this Skyrim interactive map, we'll see its full of markers that signal cities, camps and other relevant places.
Lucky for us, Leaflet makes it really easy to implement this kind of marker. In an object array, we can define what kind of markers we want to include.
const markers = [
{ id: "marker-1", position: [2502, 1070] },
{ id: "marker-2", position: [2038, 2480] },
{ id: "marker-3", position: [2048, 3578] },
];
We can define as much as we want in these objects. As an example, we can also define what kind of marker each one is.
{ id: "marker-1", position: [2502, 1070], type: "#city" },
If you only want one kind of marker, you don't have to include the type
parameter. Otherwise, it will be useful to know which SVG element to use to visualize it.
If you want to download SVG icons for fantasy markers, I recommend Game-icons.net, where you can find hundreds of high-quality icons which really fit this kind of map.
let layers;
// ...
layers = new L.LayerGroup();
for (const marker of markers) {
const myIcon = L.divIcon({
className: "place-marker",
html: `<div><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><use href="${marker.type}" /></svg></div>`,
iconAnchor: [20, 20],
});
const newMarker = L.marker(marker.position, { icon: myIcon });
layers.addLayer(newMarker);
map.addLayer(layers);
}
To start, we iterate over each of the markers in our array, and from there, we obtain the ID of the SVG we will use, which is stored under the type
property. To fetch this SVG, we make use of the use property, allowing us to use an SVG that we have already defined in our HTML.
Next, we create a marker of type DivIcon. There are extensions and plugins in Leaflet that enable you to use SVG icons directly, but for simplicity, we can do it this way for now.
Appendix π
There's lots of ways to improve a map of this kind. Using the Skyrim map we mentioned earlier as an example, you can add a navigation bar, a searchbar, ways to toggle the markers on and off, and some pop-ups that appear when you click on a marker.
For any of these, I recommend you check out the Leaflet examples and their documentation, where you can find tons of information regarding features you can add to your map.
And that's it. I hope this article is useful to you to start your adventure with fantasy map generation.