A good API minimizes the distance between intention and result

I don't care, just tell me if the layer is visible!

April 11, 2020

Mapbox-gl-js has a concept called a “style document” (or “MapStyle” as I like to call it). MapStyles are JSON objects that “define the visual appearance of a map: what data to draw, the order to draw it in, and how to style the data when drawing it”.

Here’s an example:

{
    "version": 8,
    "name": "Mapbox Streets",
    "sprite": "mapbox://sprites/mapbox/streets-v8",
    "glyphs": "mapbox://fonts/mapbox/{fontstack}/{range}.pbf",
    "sources": {},
    "layers": [
      {
        "id": "background",
        "type": "background",
        "paint": {
          "background-color": "#0C1C2B",
        },
      },
    ]
}

A common pattern used to integrate mapbox-gl-js into a React app is to store MapStyle in a Redux store. This way, a map can be modified by components that do not have access to the map instance (they dispatch an action that modifies MapStyle, the new MapStyle is loaded with map.setStyle()). There are a few issues with this approach, but the most annoying is that it requires modifying MapStyle objects directly.

MapStyles are deeply nested, composed of many different types, and lots of properties are optional. This all adds up to making code dealing with MapStyles tedious and error-prone. Say you want to know whether the background layer is visible, this can be done with:


let isVisible;
const background = mapStyle.layers.find(l => l.id === "background");

if (background) {
  if (background.layout === undefined) {
    // The background layer has no layout property, so 
    // 'layer.layout.visibility' is not set. Layers are visible by 
    // default, so the background layer must be visible.
    isVisible = true;
  }
  // The background layer has a layout property.
  isVisible = background.layout.visibility === "visible";
}

This can be shortened to:

const background = mapStyle.layers.find(l => l.id === "background");
let isVisible = background.layout === undefined || layer.layout.visibility === "visible";

Which is shorter, but cryptic.

The intention is simple and clear: “is the background layer visible?", but the code is not. To write and understand the previous examples, the developer must know:

Is all that really necessary? Imagine this dream API:

const isVisible = mapstyle
	.layers("background")
	.visible();  // true if a layer is visible

With this, the code is as simple and clear as the developer’s intention. It is also easy to extend:

mapstyle
	.layers("background")
	.visible(true);  // make the layer visible

This “fluent” style of API can be seen in jQuery and D3. Perhaps a library for operating on MapStyle objects is in order?