House Fish Balloon
  • Games
    • 1961
    • 1971
    • Trickdraw
  • Coming Soon
    • Friends Collection
    • Fashion Cents
    • Borbs
  • Blog
    • The World of Saloondria
    • HFB Tales Archive
    • Behind the Games
  • Shop
  • & | More
    • About Us
    • Contact
    • Press Resources

Behind the Games

Optimizing your Fantasy Map using Mapbox Javascript

7/24/2022

0 Comments

 
TL;DR: Using Mapbox JS documentation to add markers, toggleable layers, and raster images. Check out the map here.
by Michael Kuroda

What will I Cover?

  1. Using Map Tacks
  2. Swapping between Map Styles
  3. Using Raster Layers
Each Documentation here:
Tacks, Change Styles, Toggle Layers, Raster Layers
    A few weeks ago, I used Mapbox in an unorthodox way—to create an interactive fantasy map for the world of Saloondria (home to Trickdraw, Kickstarting until the end of July). This meant exporting GEOJson data from Azgaar’s Fantasy Map Generator, coloring them by height and biome in Mapbox, and embedding the map onto our website. It was a fun process, and I got a lot of encouraging comments wherever I posted it! I was encouraged to develop it beyond what I had before
​    This time, however, I am mostly going to guide you through how to develop your Mapbox HTML on your website. This part is a lot more code-heavy than my previous post. Anyone can copy/paste one of these segments for their own fantasy map, but I recommend having a basic understanding of coding before you attempt to merge these segments together. 

Using Map Tacks

The Mapbox Documentation can be found here. It explains how to do what I'm doing below pretty well.
Picture
    Map Tacks are an awesome way to show off points of interest. For me, those points of interest are my Saloondria Short Story locations, as well as posts to Reddit.com/r/Worldbuilding. Both of which look into the lore behind the world of Saloondria and are usually centered around certain cities or regions.
    This endeavor was definitely the hardest when I first started working with Mapbox documentation. It requires delving straight into some of the major Mapbox features. What will you need to do to create your own map tacks? 

Style your Popup Box

Picture
    ​Step one is to create the Popup style. What will it look like when you click on one of the many markers on your map? Here is an example of how I formatted mine. (style tags removed to show on weebly)
 
    .mapboxgl-popup {
        max-width: 390px;
         font: 12px/20px 'Helvetica Neue', Arial, Helvetica, sans- 
         serif;
     }
 

Adding a Source For Your Markers

    ​With the popup style out of the way, you can begin creating your geojson data. I created mine in HTML on my website, but you can also import your tacks and tack locations if you have that available. This is an example of one data point added to my “places” geojson source:

map.addSource('places', {
            'type': 'geojson',
            'data': {
                'type': 'FeatureCollection',
                'features': [
                    {
                        // Hatbrim Council
                        'type': 'Feature',
                        'geometry': {
                            'type': 'Point',
                            'coordinates': [0.481724, 41.875105]
                        },
                        'properties': {
                            'title': 'City of Hatbrim',
                            'image':'INSERT POPUP HTML HERE',
                        }
                    },

  Breaking apart this code, I am creating a single feature for the geojson data set. This feature could be as as simple as ‘type’: ‘Point’, ‘coordinates’:[0.4, 41.8]. However, for my popup window, I attached an image, some text, and a link. This is a miniature HTML group, all nested within the properties section.

Create a Layer From Source

      Next up, we need to make these tacks into an actual layer. Create a layer using map.addLayer({});. Within the layer, you should add an ID, type, source, and layout. Here is how I formatted mine:
Adding the layer:

        map.addLayer({
            'id': 'Saloondria Short Stories',
            'type': 'symbol',
            'source': 'places',
            'layout': {
                'icon-image': 'custom-marker',
                'visibility': 'visible'
            }
                    
        });   

Adding the custom marker image:

        map.loadImage(
            'https://housefishballoon.com/uploads/1/3/3/5/133560731/published/fishpin.png?1654512979',
            (error, image) => {
                if (error) throw error;
                map.addImage('custom-marker', image);
            }

    The id and visibility will later be used to toggle the map tacks on and off. Meanwhile, the icon-image is linked to a custom image I loaded using map.loadImage(). 

Showing Information on Click

Showing the information you set up when the user clicks on a Map Tack is a matter of creating a map.on('click') event, setting the coordinates to equal that of the map tack, and initializing the popup with the html info you created earlier. This section of code also changes the user's cursor to indicate that a map tack is clickable:

map.on('click',['Saloondria Short Stories','Worldbuilding Posts'], (e) => {
            
            map.flyTo({
                center: e.features[0].geometry.coordinates
            });
            // Copy coordinates array.
            const coordinates = e.features[0].geometry.coordinates.slice();
            //const description = e.features[0].properties.description;
            const image = e.features[0].properties.image;

            // Ensure that if the map is zoomed out such that multiple
            // copies of the feature are visible, the popup appears
            // over the copy being pointed to.
            while (Math.abs(e.lngLat.lng - coordinates[0]) > 180) {
                coordinates[0] += e.lngLat.lng > coordinates[0] ? 360 : -360;
            }

            new mapboxgl.Popup()
                .setLngLat(coordinates)
                .setHTML(image)
                .addTo(map);
        });

        // Change the cursor to a pointer when the mouse is over the places layer.
        map.on('mouseenter', ['Saloondria Short Stories','Worldbuilding Posts'], () => {
            map.getCanvas().style.cursor = 'pointer';
        });

        // Change it back to a pointer when it leaves.
        map.on('mouseleave', ['Saloondria Short Stories','Worldbuilding Posts'], () => {
            map.getCanvas().style.cursor = '';
        });
        
        // If these two layers were not added to the map, abort
        if (!map.getLayer('Saloondria Short Stories') || !map.getLayer('Worldbuilding Posts')) {
            return;
        }


    This map.on('click') function allows for the map tacks to toggle the popups we designed earlier in this section. I supplemented this by adding a map.flyTo({}) section. Historymaps was a huge inspiration for that addition.

Swapping Between Map Styles

    Again, the Mapbox Documentation can be found here and here. Skip this step if you're satisfied.
Picture
    Map Styles is an interesting step, and the biggest change I made from the previous map version to the current version. It adds depth to your world by allowing the user to swap between different display options. Maybe they want to see what your geography looks like. Maybe they also want to know the religion, or the political intrigue? This can be done in two ways: By adding all of the information on one, very dense, map. Or by allowing the user to toggle between different maps.
    To do this, you will just need to understand two major functions. A toggle system for layers that can be turned on and off, and a switch that disables other layers when it is enabled. 

Creating the Menu

Lets start by creating the menu itself. This will be the GUI that the user can click on to change which map style is enabled and which map layers are visible.
Adding the Menu style to our <style>

    #menu {
        background: #fff;
        position: absolute;
        z-index: 1;
        top: 10px;
        right: 10px;
        border-radius: 3px;
        width: 120px;
        border: 1px solid rgba(0, 0, 0, 0.4);
        font-family: 'Open Sans', sans-serif;
    }

    #menu a {
        font-size: 13px;
        color: #404040;
        display: block;
        margin: 0;
        padding: 0;
        padding: 10px;
        text-decoration: none;
        border-bottom: 1px solid rgba(0, 0, 0, 0.25);
        text-align: center;
    }

    #menu a:last-child {
        border: none;
    }

    #menu a:hover {
        background-color: #f8f8f8;
        color: #404040;
    }

    #menu a.active {
        background-color: #404040;
        color: #e5d4a8;
    }

    #menu a.active:hover {
        background: #3074a4;
    }

Create Menu using our style guide.

        for (const id of LayerIds) {
            // Skip layers that already have a button set up.
            if (document.getElementById(id)) {
                continue;
            }

            // Create a link.
            const link = document.createElement('a');
            link.id = id;
            link.href = '#';
            link.textContent = id;

            link.className = 'active'

Picture

Adding Toggleable Layers

    Remember the layer name and visibility we used in the previous section? Toggling a layer style is about as simple as enabling and disabling that layer's visibility. (swapping between 'visibility':'visible', and 'visibility':'null'.
How I added toggle layers:

link.onclick = function (e) {
   const clickedLayer = this.textContent;
   e.preventDefault();
   e.stopPropagation();
                
   if (toggleableLayerIds.includes(clickedLayer)){
      const visibility = map.getLayoutProperty(
      clickedLayer,
      'visibility'
   );

   // Toggle layer visibility by changing the layout object's visibility property.
   if (visibility === 'visible') {
      map.setLayoutProperty(clickedLayer, 'visibility', 'none');
      this.className = '';
   } else {
      this.className = 'active';
      map.setLayoutProperty(
         clickedLayer,
         'visibility',
         visible'
      );
   }

Note: The basic Mapbox Documentation does not consider swapping layer styles, as well as making layers visible and invisible. To address this, I added a variable "toggleableLayerIds" which contains the layers that can be swapped on and off.

Adding Map Style Selection

However, I want to push things a little further today. Layers are fun to enable and disable, but we still need to show off the various maps that a user can switch between. Lets add another style! 
Picture
    Just copy and paste your mapbox style. Using the second version, you can edit the colors of your cells based on something else. I chose "Country ID" for one, and "Religion ID" for another. This gives us a couple more style options to choose from.
    With links to your new styles, you just need to create a function that lets you toggle styles on and off. The trickiest part of this would be disabling the other styles, and toggling your main style on and off. Aside from this, its as easy as a few "if" statements, and a for loop if you have more than a few styles to work with.
How I implemented this:

else{
 if(clickedLayer == 'Religious Map'){
    if (this.className == ''){
       map.setStyle(RELIGIOUS MAP STYLE HERE');
       document.getElementById('Country Map').className = '';
       document.getElementById('Illustrated Map').className = '';
       document.getElementById('Geographic Map').className = '';
       this.className = 'active';
    } else{
       this.className='';
       map.setStyle('DEFAULT STYLE HERE');
       document.getElementById('Geographic Map').className = 'active';
    }
}

    With that finished, you should have a functioning menu to swap between styles! Congrats!
NOTE: If you decide to change map styles with your pins, you should make sure that your pins only load when the map style loads: map.on('style.load', () => {, as opposed to on initial load. Otherwise they will disappear when you change the style.

Raster Layers

    If you finished the previous steps, you should be a master of raster layers. Here is the Mapbox documentation for this step.
Picture

Raster Layer Example

    Adding raster layers is actually much simpler than the previous steps. The main difficulty lies in finding the exact coordinates you want to set each corner of an image at. If an image is a square, you can just move each coordinate the same distance each time. However, if its a rectangle, you may need to do a bit of math (or trial and error) to reduce distortion.
Adding the Raster Layer:

        map.addSource('Sea-Monster', {
            'type': 'image',
            'url': 'https://i.imgur.com/yUBlrGs.gif',
            'coordinates': [
                [-1.416139, 42.011105],
                [-1.116139, 42.011105],
                [-1.115966, 41.711105],
                [-1.416139, 41.711105] 
            ]
        });

        map.addLayer({
            id: 'sea-monster-layer',
            'type': 'raster',
            'source': 'Sea-Monster',
            'paint': {
                'raster-fade-duration': 0
            }
        });

    Working with the Raster Layer itself is as easy as importing an image link. .gifs seem to work the best. Using a link to the actual .gif image, you can import that as the url, and give it your desired corner coordinates. (you can find coordinates on your mapbox editor if necessary).

Toggleable Raster Layer

Lastly, if you desire to toggle a raster layer the same way you toggle your pin layers and map styles, it is not too difficult. Just make sure to set a layout style, with visibility set to "none." This way, if you update your map with every style change, this isn't the first thing that shows up!
Adding layout to allow for toggling.

        map.addLayer({
            id: 'Illustrated Map',
            'type': 'raster',
            'source': 'Old-Map',
            'paint': {
                'raster-fade-duration': 0
            },
            'layout': {
                'visibility': 'none'
            }
        });

In Conclusion

    Hope that this guide can help any worldbuilders out there. It took a bit of searching on my part to find these particular documentation pages to work from, but each part of it certainly adds to my fantasy map of Saloondria. If any part of this is confusing you, just let me know in the comments and I will try to address it in an updated blog post, or in the comment section.
    And if you are in need of a game to play after reading through all of this, I suggest checking out Trickdraw (on Kickstarter until August 1st)
Picture
0 Comments



Leave a Reply.

    Picture

    Archives

    March 2023
    September 2022
    August 2022
    July 2022
    June 2022
    December 2021

    Categories

    All
    Author: Blake Propach
    Author: Michael Kuroda
    Category: Game Design
    Category: Tutorial

    RSS Feed

the House
About Us
Our Friends
​
Work With Us​
the Fish
Behind the Games
HFB Tales Archive
Fish School Discord
the Balloons
1961: A Cold War Card Game
The World of Saloondria
& More
Contact
​Policies​
Retailer Resources
Press Resources

© 2023 House Fish Balloon, LLC.
info@housefishballoon.com
​Site powered by love.

Stay in touch!
  • Games
    • 1961
    • 1971
    • Trickdraw
  • Coming Soon
    • Friends Collection
    • Fashion Cents
    • Borbs
  • Blog
    • The World of Saloondria
    • HFB Tales Archive
    • Behind the Games
  • Shop
  • & | More
    • About Us
    • Contact
    • Press Resources