DEV Community

Cover image for Effectively switching mapbox-gl styles
dqunbp
dqunbp

Posted on • Edited on

4 1

Effectively switching mapbox-gl styles

Hi there!

I wish write about known common problem with mapbox-gl.
At the moment there are some proposals for a solution and some workarounds.

In this article, we see the typical scenario and going to go to implement it.

Conditions of the problem

Let's describe a simple case:

  • We need to add to the map our own GeoJSON source and corresponding layer, before the text labels.
  • When switching the base style, our layer should remain on the map.

We use a straightforward approach with mapbox-gl events.

Define the source

// constants.js
const GEOJSON_FEATURE = {
  type: "Feature",
  properties: {},
  geometry: {
    type: "Polygon",
    coordinates: [
      [
        [-66.96466, 44.8097],
        [-67.79035274928509, 47.066248887716995],
        [-69.23708614772835, 47.44777598732787],
        [-71.08482, 45.3052400000002],
        [-70.64573401557249, 43.090083319667144],
        [-66.96466, 44.8097],
      ],
    ],
  },
};
Enter fullscreen mode Exit fullscreen mode

Create the map

// index.js
mapboxgl.accessToken = <your token here>;
var map = new mapboxgl.Map({
  container: "map",
  style: "mapbox://styles/mapbox/streets-v11",
  center: [-68.13734351262877, 45.137451890638886],
  zoom: 5,
});
Enter fullscreen mode Exit fullscreen mode

Improve addLayer function

If we use beforeId that not exists already on the map, we get an error
Let's create addLayerBefore function to escape this

function addLayerBefore(addLayerFn, layer, beforeId) {
// check beforeId defined and exists on the map
const beforeLayer = Boolean(beforeId) && map.getLayer(beforeId);
  if (beforeLayer && beforeId === beforeLayer.id) addLayerFn(layer, beforeId);
  else {
    console.warn(
      `Not found layer with id '${beforeId}'.\nLayer '${layer.id}' added without before.`
    );
    addLayerFn(layer);
  }
}
Enter fullscreen mode Exit fullscreen mode

Define add data function

Add's GeoJSON source with fill layer, before waterway-label in current example it places polygon before the map text labels

function addMaineLayer() {
  map.addSource("maine", {
    type: "geojson",
    data: {
      type: "Feature",
      geometry: GEOJSON_FEATURE
  });

  // define the function to add layer
  const addLayer = (layer, beforeId) => map.addLayer(layer, beforeId);

  addLayerBefore(
    addLayer,
    {
      id: "maine",
      type: "fill",
      source: "maine",
      layout: {},
      paint: {
        "fill-color": "#088",
        "fill-opacity": 0.8
      }
    },
    "waterway-label"
  );
}
Enter fullscreen mode Exit fullscreen mode

Setup events

We should call addMaineLayer function when:

  • map initially loads with load event
  • map changes the style with styledata event
map.on("load", function () {
  // add layer to the map on load
  addMaineLayer();

  const layerList = document.getElementById("menu");
  const inputs = layerList.getElementsByTagName("input");

  function switchLayer(layer) {
    // addMaineLayer fn will be called once on layer switched
    map.once("styledata", addMaineLayer);
    const layerId = layer.target.id;
    map.setStyle("mapbox://styles/mapbox/" + layerId);
  }

  // set toggle base style events
  for (let i = 0; i < inputs.length; i++) {
    inputs[i].onclick = switchLayer;
  }
});
Enter fullscreen mode Exit fullscreen mode

Let's check the result

That is!
In the next article we going to go implement this with RxJS!

Heroku

This site is built on Heroku

Join the ranks of developers at Salesforce, Airbase, DEV, and more who deploy their mission critical applications on Heroku. Sign up today and launch your first app!

Get Started

Top comments (0)

Billboard image

The Next Generation Developer Platform

Coherence is the first Platform-as-a-Service you can control. Unlike "black-box" platforms that are opinionated about the infra you can deploy, Coherence is powered by CNC, the open-source IaC framework, which offers limitless customization.

Learn more