bertin

logo

npm jsdeliver license code size github stars

Bertin.js is a JavaScript library for visualizing geospatial data and make thematic maps for the web.

The project is under active development. Some of features and options are subject to change. We welcome contributions of all kinds: bug reports, code contributions and documentation. A todo list of the next improvements is available here.

bertin is an easy to use JavaScript library mainly based on D3.js makes creating thematic maps simple. The principle is to work with layers stacked on top of one other. Much like in Geographic Information Software (GIS) software, Bertin.js displays layers with a specific hierarchy. The layer at bottom are rendered and then followed by the layer right above it. Some of the layers are used to display various components of a map, some of common layers are: header, footer, graticule, outline, choro, typo, prop, shadow, scalebar, text etc.

Cheat Sheet

Who is Bertin?

Jacques Bertin (1918-2010) was a French cartographer, whose major contribution was a theoretical and practical reflection on all graphic representations (diagrams, maps and graphs), forming the subject of a fundamental treatise, Graphic Semiology, originally published in 1967. Bertin’s influence remains strong not only in academics, teaching of cartography today, but also among statisticians and data visualization specialists.

Installation

In browser

Latest version

<script
  src="https://cdn.jsdelivr.net/npm/bertin"
  charset="utf-8"
></script>

Pinned version

<script src="https://cdn.jsdelivr.net/npm/bertin@1.7" charset="utf-8"></script>

In Observable

Latest version

bertin = require("bertin");

Pinned version

bertin = require("bertin@1.7");

In Quarto

In Quarto, you can use bertin with ojs cells. This allows to combine analyses in R or Python with visualizations made with bertin. An example is available here

Usage

In browser

<script src="https://cdn.jsdelivr.net/npm/d3@7"></script>
<script src="https://cdn.jsdelivr.net/npm/bertin"></script>

<script>
  let geojson =
    "https://raw.githubusercontent.com/neocarto/bertin/main/data/world.geojson";

  d3.json(geojson).then((r) =>
    document.body.appendChild(
      bertin.draw({
        params: {
          projection: "VanDerGrinten4",
          clip: true,
        },
        layers: [
          { geojson: r, tooltip: ["$ISO3", "$NAMEen"] },
          { type: "outline" },
          { type: "graticule" },
        ],
      })
    )
  );
</script>

See examples: Example 1, Example 2 and Example 3.

In Observable Notebook

The bertin.js library is really easy to use within an Observable notebook. You’ll find many examples in this notebook collection. Feel free to fork, copy, modify with your own data.

Drawing a map

draw() is the main function of the library. It allows you to make various thematic maps. It allows to display and overlay different types of layers listed below. The layers written on top are displayed first. Source Example

Global parameters

In the section params, we define the global parameters of the map: its size, projection, background color, etc. To have access to a large number of projections, you will need to load the d3-geo-projection\@4 library. This section is optional.

Code

bertin.draw({
  params: {
    projection: d3.geoBertin1953(),
    width: 750,
  },
  layers: [...]
})

A globe view is available for some types of layers: simple (with symbol_shift == 0), spikes, bubble (with dorling == true), geolines, graticule, inner, missing, shadow, tissot, dotdensity and label.

To use it, you juste have to use "globe" as projection. You can also define the center og the map by using "globe(x,y,rotate)"

Parameters

Map types

Simple layer

The layer type allows to display a simple geojson layer (points, lines or polygons). Source. Example 1 and Example 2.

Code

bertin.draw({
  layers: [
    {
      type: "layer",
      geojson: *a geojson here*,
      fill: "#e6acdf",
    }
  ]
})

Parameters

Parameters of the legend

Choropleth

The choro type aims to draw Choropleth maps. This kind of representation is especially suitable for relative quantitative data (rates, indices, densities). The choro type can be applied to the fill or stroke property of a simple layer. Example.

Code

bertin.draw({
  layers: [
    {
      type: "layer",
      geojson: data,
      fill: {
        type: "choro",
        values: "gdppc",
        nbreaks: 5,
        method: "quantile",
        colors: "RdYlGn",
        leg_round: -1,
        leg_title: `GDP per inh (in $)`,
        leg_x: 100,
        leg_y: 200
      }
  ]
})

Parameters

Parameters of the legend

Typology

The typo type allows to realize a qualitative map. The choro type can be applied to the fill or stroke property of a simple layer. Example.

Code

bertin.draw({
  layers: [
    {
      type: "layer",
      geojson: data,
      fill: {
        type: "typo",
        values: "region",
        pal: "Tableau10",
        tooltip: ["$region", "$name"],
        leg_title: `The Continents`,
        leg_x: 55,
        leg_y: 180
      }
    }
  ]
})

Parameters

Parameters of the legend

Bubble

The bubble type is used to draw a map by proportional circles. Source, Example.

Code

bertin.draw({
  layers: [
    {
      type: "bubble",
      geojson: countries,
      values: "pop",
      k: 60,
      tooltip: ["$country", "$pop", "(inh.)"],
    },
  ],
});

Parameters

Parameters of the legend

Square

The square type is used to draw a map by proportional squares. Source, Example.

Code

bertin.draw({
  layers: [
    {
      type: "square",
      geojson: countries,
      values: "pop",
      k: 60,
      tooltip: ["$country", "$pop", "(inh.)"],
    },
  ],
});

Parameters

Parameters of the legend

Regular Bubble

The regularbubble type is used to draw a map by proportional circles in a regular grid, from absolute quantitative data. Source, Example and methodology.

Code

bertin.draw({
  layers: [
    {
      type: "regularbubble",
      geojson: countries,
      step:20,
      values: "pop",
      k: 60,
      tooltip: "$value",
    },
  ],
});

Parameters

All other parameters are the same as for the bubble layer

Regular Square

The regularsquare type is used to draw a map by proportional squares in a regular grid, from absolute quantitative data. Source, Example and methodology.

Code

bertin.draw({
  layers: [
    {
      type: "regularsquare",
      geojson: countries,
      step:20,
      values: "pop",
      k: 60,
      tooltip: "$value",
    },
  ],
});

Parameters

All other parameters are the same as for the square layer

Regular grid

The regulargrid type is a way to transform an irregular geographic mesh into a regular mesh. The values of the grid cells are obtained in proportion to the intersected surface. This representation mode is only suitable for absolute quantitative data. But you can use 2 quantitative data to compute a ratio Source, Example and methodology.

Code

bertin.draw({
  layers: [
    {
      type: "regulargrid",
      geojson: countries,
      step:20,
      values: "pop",
      fill:{
        nbreaks: 6,
        method: "quantile",
        colors: "Blues"
      }
      tooltip: "$value",
    },
  ],
});

Parameters

All other parameters are the same as for choropleth maps

Stock and ratio

In thematic mapping, we often have to represent an absolute quantitative data with a size variation and relative quantitative data with color variations. For this we can use the bubble type and the choro type together. Example.

Code

bertin.draw({
  params: { projection: d3.geoPolyhedralWaterman() },
  layers: [
    {
      type: "bubble",
      geojson: data,
      leg_round: -2,
      values: "pop",
      fill: {
        type: "choro",
        method: "quantile",
        nbreaks: 5,
        values: "gdppc",
        pal: "RdYlGn",
      },
    },
  ],
});

Stock and typo

In thematic mapping, we often have to represent an absolute quantitative data with a size variation and relative quantitative data with color variations. For this we can use the bubble type and the typo type together. Example.

Code

bertin.draw({
  layers: [
    {
      type: "bubble",
      geojson: data,
      values: "pop",
      fill: {
        type: "typo",
        values: "region",
      },
    },
  ],
});

Dorling cartogram

The dorling parameter can be used with the bubble type to design a Dorling cartogram. Example.

Code

bertin.draw({
  layers: [
    {
      type: "bubble",
      geojson: data,
      values: "pop",
      k: k,
      dorling: true,
      iteration: 100,
      fill: "#E95B40",
    },
  ],
});

Dot cartogram

The dotcartogram type is a method of map representation that follows Dorling’s cartograms and dot density maps. The data from each territorial unit are dissolved in such a way that a dot represents a constant quantity, the same on the whole map. Source Example.

Code

bertin.draw({
  params: { projection: d3.geoBertin1953() },
  layers: [
    {
      type: "dotcartogram",
      geojson: data,
      onedot: 200000000000,
      iteration: 200,
      values: "gdp",
      radius: radius,
      span: span,
      leg_fill: "none",
      leg_stroke: "black",
      leg_strokeWidth: 1.5,
      leg_x: 800,
      leg_y: 450,
      leg_title: `GDP by world region`,
      leg_txt: "200 billion $",
      fill: "red",
      tooltip: ["$name", "$region"],
    },
  ],
});

Parameters

Parameters of the legend

Mushroom

The mushroom type is used to draw a map with 2 superposed proportional semi-circles. This type of representation can be used when two data with the same order of magnitude need to be compressed. Source, Example.

Code

bertin.draw({
  layers: [
    {
      type: "mushroom",
      geojson: mygeojson,
      top_values: "gdp_pct",
      bottom_values: "pop_pct",
      bottom_tooltip: ["name", "pop", "(thousands inh.)"],
      top_tooltip: ["name", "gdp", "(million $)"],
    },
  ],
});

Parameters

Parameters of the legend

Dot Density

The dotdensity type allows to display a doty density layer geojson layer from polygons and attribute data. Source. Example

Code

bertin.draw({
  layers: [
    {
      type: "dotdensity",
      geojson: *a geojson here (polygon)*,
      values: *a fiel here*
      dotvalue: *a number*,
    }
  ]
})

Parameters

Parameters of the legend

Spikes

The spikes type is used to draw a map with spikes. Source, Example.

Code

bertin.draw({
  layers: [
    {
      type: "spikes",
      geojson: countries,
      values: "pop",
      k: 60,
      w: 8,
      tooltip: ["$country", "$pop", "(inh.)"],
    },
  ],
});

Parameters

Parameters of the legend

Ridge

The ridge type allows to produce 2.5D maps by varying the height of lines. The method takes as input polygons or multi polygons. Source Example.

Code

bertin.draw({
  layers: [
 {
      type: "ridge",
      geojson: data,
      values: "pop",
      step: 30,
      blur: 0.4,
      k: 50
    },
  ],
});

Parameters

Smooth

The smooth type (or heatmap or contour) is a way to produce a continuous représentations from quantitative data. The algorithm is complex. The values produced do not really make sense. Explanations with the parameters. Source, Example and methodology.

Code

bertin.draw({
  layers: [
 {
      type: "smooth",
      geojson: data,
      values: "pop",
      thresholds: 50,
      bandwidth: 25,
      colorcurve: 1,
    },
  ],
});

Parameters

Contour parameters

By default, the smooth layer is calculated from dots or centroids. But it is possible to go through a regular grid by using theses parameters.

Thickness

On each layer, you can dynamically vary the thickness of the paths. This can be useful to make for example flow maps or discontinuity maps. Source

Code - Constant Thickness

In order for each object to have the same thickness.

bertin.draw({
  layers: [
    {
      type: "layer",
      geojson: *a geojson here*,
      strokeWidth: 3,
    }
  ]
})

Code - Linear variation

To vary the thickness proportionally to an quantitative data.

bertin.draw({
  layers: [
    {
      type: "layer",
      geojson: *a geojson here*,
      strokeWidth: {type:"linear",values:"migration"},
    }
  ]
})

Parameters

Code - Discrete variation

To vary the thickness according to classes values

bertin.draw({
  layers: [
    {
      type: "layer",
      geojson: *a geojson here*,
      strokeWidth: {type:"discr",values:"migration", method: "q6"},
    }
  ]
})

Parameters

Code - Categories

To vary the thickness according to qualitative data.

bertin.draw({
  layers: [
    {
      type: "layer",
      geojson: *a geojson here*,
      strokeWidth: {type:"quali",values:"flow", categories: ["low", "medium", "high", "very high"]},
    }
  ]
})

Parameters

Parameters of the legends

Map components

The footer type allows to display text under the map. This is useful to display sources. Source.

Code

bertin.draw({
  layers: [
    {
      type: "footer",
      text: "Source: Worldbank, 2021",
      fontSize: 10,
    },
  ],
});

Parameters

Geolines

The geolines type allows you to display geographic lines (equator, tropics, polar circles). Source.

Code

bertin.draw({
  layers: [
    {
      type: "geolines",
      stroke: "black",
      strokeWidth: [5, 3, 1],
    },
  ],
});

Parameters

For each parameter, you can set a single value for all lines are an array in that order: [equator, tropics, polar].

Graticule

The graticule type allows you to display the latitude and longitude lines around . Source.

Code

bertin.draw({
  layers: [
    {
      type: "graticule",
      stroke: "#644580",
      step: [20, 10],
    },
  ],
});

Parameters

hatch (or hatching)

The hatch type only allows to add hatchings on the whole page to make it a bit prettier. Source.

Code

bertin.draw({
  layers: [
    {
      type: "hatch",
      angle: 45,
    },
  ],
});

Parameters

Inner

The inner type allows to display a strip inside the polygon. This inner area can be used to draw preet maps with a vintage style.

Code

bertin.draw({
  layers: [
    {
      type: "inner",
      thickness: 5,
      fill:"white",
      blur:4
    },
  ],
});

Parameters

Minimap (location map)

The minimap type (or location) allows to display a location map showing the coverage of the map. Source. Example

bertin.draw({
  layers: [
    {
      type: "minimap",
      x: 50,
      y: 50,
      width:300,
      frame:{fill: "yellow", stroke: "none"}
    },
  ],
});

Parameters

Rhumbs

The rhumbs type allows to display “rhumb lines” like on old portolan charts. Source.

Code

bertin.draw({
  layers: [
    {
      type: "rhumbs",
      position: [370, 370],
      nb: 25
    },
  ],
});

Parameters

Water lines (slow)

The waterlines type only allows to display lines spaced by a defined distance. It’s just a graphic trick to make the maps look nice. Source.

Code

bertin.draw({
  layers: [
    {
      type: "waterlines",
      geojson: world,
      dist: 100,
      nb: 5
    },
  ],
});

Parameters

The logo type only allows allows to display a logo on the map from an url. By default, the bertin.js logo is displayed. Source Example.

Code

bertin.draw({
  layers: [
    {
      type: "logo",
      url: "http://myimage.png"
      position: "left",
    },
  ],
});

Parameters

The header type allows to display a title above the map. Source.

Code

bertin.draw({
  layers: [
    {
      type: "header",
      text: "Title of the map",
      fontSize: 40,
    },
  ],
});

Parameters

Labels

The label type allows to display labels from a geojson. Source, Example.

Code

bertin.draw({
  layers: [
    {
      type: "label",
      geojson: countries,
      values: "name",
    },
  ],
});

Parameters

Missing

The missing type displays any missing data when creating a map by proportional symbols. The highlight of graphical elements (in white under the symbols) allows for clear data comprehension of gaps in data. Source.

Code

bertin.draw({
  layers: [
    {
      type: "missing",
      geojson: countries,
      values: "pop"
  ]
})

Parameters

Parameters of the legend

Outline

The outline type is used to display the limits of the earth area in the given projection. Source.

Code

bertin.draw({
  layers: [
    {
      type: "outline",
      fill: "#4269ad",
    },
  ],
});

Parameters

Path

The path type is used to display any svg path Source Example

Code

bertin.draw({
  layers: [
    {
      type: "path",
      d: "m 99.330229,64.978319 -9.724281,-1.057429 -3.946379,8.950188 -3.94638,-8.950188 -9.72428,1.057429 5.7779,-7.89276 -5.7779,-7.892759 9.72428,1.05743 3.94638,-8.950189 3.94638,8.950189 9.72428,-1.057429 -5.777901,7.892759 z",
      fill:"blue",
      sroke: "none",
      scale: 2

    },
  ],
});

Parameters

Scalebar

The scalebar type allows to display a scale bar in miles or kilometers. Source.

Code

bertin.draw({
  layers: [
    {
      type: "scalebar",
      units: "miles",
    },
  ],
});

Parameters

Shadow

The shadow type allows to display a shadow under a layer to give it a relief effect. Source.

Code

bertin.draw({
  layers: [
    {
      type: "shadow",
      geojson: JPN,
      dx: 5,
      dy: 5,
    },
  ],
});

Parameters

Texts

The text type simply allows you to display text anywhere on the map. Source. Example.

Code

bertin.draw({
  layers: [
    {
      type: "text",
      text: "This is my text",
      position: "bottomright",
      fontSize: 20,
      frame_stroke: "red",
      margin: 4,
    },
  ],
});

Parameters

Mercator Tiles

The tiles type allow to display a raster basemap. Source. Example.

NB: It works only with the d3.geoMercator() projection. if tiles layer is used is the draw function, the projection is automaticaly setted to d3.geoMercator(). And you can’t change it.

Code

bertin.draw({
  params: { 
            projection: d3.geoMercator(),
            extent: *a geojson*  
          },
  layers: [
    {
      type: "tiles",
      style: "worldphysical"
    },
  ],
});

Parameters

style: {
  provider: "OpenStreetMap contributors", 
  url: (x, y, z) => `https://tile.openstreetmap.org/${z}/${x}/${y}.png`,
   }

Tissot’s indicatrix

The tissot type aims to draw Tissot circles to visualize the deformations due to the projection Source.

Code

bertin.draw({
  layers: [
    {
      type: "tissot",
      step: 20
    },
  ],
});

Parameters

Custom Layer

The custom type (or function type) allows you to provide your own render function to create custom layer. [Example]

Code

bertin.draw({
  layers: [
    {
      type: "custom",
      render: function (svg, map) {
        svg
          .append("g")
          .append("rect")
          .attr("x", map.width / 2)
          .attr("y", map.height / 2)
          .attr("height", 100)
          .attr("width", 200)
          .style("fill", "red");
      }
    },
    { geojson: world, fill: "white" },
    { type: "graticule" },
    { type: "outline" }
  ]
})

Or with an external function

function drawRectangle(svg, map, options) {
  const { fill, stroke, strokeWidth } = options; // Will have all options (parameters) set in the curret layer object, right?
  svg
    .append("g")
    .append("rect")
    .attr("x", map.width / 2)
    .attr("y", map.height / 2)
    .attr("height", 100)
    .attr("width", 200)
    .style("fill", fill)
    .style("stroke", stroke)
    .style("stroke-width", strokeWidth);
}

then:

bertin.draw({
  params: { extent: world, width: 1000 },
  layers: [
    {
      type: "function",
      render: drawRectangle,
      fill: "red",
      stroke: "blue",
      strokeWidth: 10
    },
    { geojson: world, fill: "white" },
    { type: "graticule" },
    { type: "outline" }
  ]
})

with

map.width: the width of the map map.height: the height of the map
map.projection: the projection of the map map.clipid: the unique id of the map

Geojson properties selections

properties.add

properties.add allows to add a new field in the att ribute table. This function return a new object and do not modify the initial object. Example. Code.

Code

bertin.properties.add({
    geojson: world, 
    field: "gdppc", 
    expression: "gdp/pop*1000" 
})

Parameters

properties.filter

properties.filter allows to filter a geojson from its attribute table.This function return a new object and do not modify the initial object. Example. Code.

Code

bertin.properties.filter({
    x: world,
    expression: "pop2022 >= 100000" 
})

Parameters

properties.head

properties.head allows to get the n top values from a given field.This function return a new object and do not modify the initial object. Example. Code.

Code

bertin.properties.head({
    geojson: world,
    field: "gdp",
    nb: 5
})

Parameters

properties.keep

properties.keep allows to select one or several columns to keep in the attribute table. All other columns are deleted. This function return a new object and do not modify the initial object. Example. Code.

Code

bertin.properties.keep({
    geojson: world,
    field: ["ISO3", "pop2020"] 
})

Parameters

properties.remove

properties.remove allows to remove one or several columns in the attribute table. This function return a new object and do not modify the initial object. Example. Code.

Code

bertin.properties.remove({
    geojson: world,
    field: ["tmp", "FID"] 
})

Parameters

properties.subset

properties.subset allows to remove one or several columns in the attribute table. This function return a new object and do not modify the initial object. Example. Code.

Code

bertin.properties.subset({
    geojson: world,
    field: "ISO3",
    selection:   ["USA", "CAN", "MEX"],
    inverse: false
})

Parameters

properties.table

properties.subset allows to get a geojson attribute table Example. Code.

Code

bertin.properties.table(*a geojson*)

properties.head

properties.tail allows to get the n bottom values from a given field. This function return a new object and do not modify the initial object. Example. Code.

Code

bertin.properties.tail({
    geojson: world,
    field: "gdp",
    nb: 5
})

Parameters

Other functions

borders

borders is a function that extract borders from polygons, with ids. Source

Code

bertin.borders({geojson: world, id: "iso3", values: "population", type = "rel"})

Parameters

bbox

bbox computes a geojson object form an array defining an extent in latitude and longitude.

Code

bertin.bbox([
  [112, -43],
  [153, -9],
]);

Quickdraw

quickdraw function displays one or more layers directly and easily. Source, Example.

Code

bertin.quickdraw(geojson);
bertin.quickdraw(geojson, 1000, 7);

Parameters

Match

match() is a function to evaluate the quality of a join between the data and the background map. It returns a chart. Source, Example.

Code

let testjoin = bertin.match(countries, "ISO3_CODE", maddison, "countrycode");

.matched returns an array containing matched ids

testjoin.matched;

.matched_data returns an array containing matched data ids

testjoin.matched_data;

.unmatched_data returns an array containing unmatched data ids

testjoin.unmatched_data;

.unmatched_geom returns an array containing unmatched geom ids

testjoin.unmatched_geom;

Parameters

Merge

merge is a function to join a geojson and a data file. This is the first step in the mapping process. Source, Example.

Code

const data = bertin.merge(
  countries,
  "ISO3_CODE",
  maddison,
  "countrycode",
  true
);

Parameters

links is a function that create links from geometries (polygons or points) and a data file (i,j,fij). Source Example.

Code

bertin.links({
  geojson: world,
  geojson_id: "ISO3",
  data: migr2019,
  data_i: "i",
  data_j: "j",
});

Parameters

subgeo

Depreciated. See properties.subset/

table2geo

table2geo function converts a data table with lat/lon fields or a coords field to a geojson. Source, Example.

Code

bertin.table2geo(cities, "lat", "lng");

Or

bertin.table2geo(cities, "coords");

Parameters

Info

After creating a map, you can have access to the map info (width, total height, map height, footer height, header height, projection).

Code

mymap = bertin.draw({
  params: {
    projection: d3.geoBertin1953(),
    width: 750,
  },
  layers: [...]
})

then

mymap.info

Observable viewof

Within Observable, you can use bertin maps as Inputs by using viewof. In Observable, a view is a user interface element that directly controls a value in the notebook. See explanations here: https://observablehq.com/@observablehq/views.

In bertin, you can define which layer(s) are used by specifying the viewof parameter at the layer level. It works for simple, bubble, square, dotcartogram, mushroom, regularbubble, regularsquare, regulargrid and spikes layer.

Code

viewof map = bertin.draw({
  layers: [
    {
      type: "bubble",
      geojson: *a geojson*,
      values:"pop",
      viewof: true
    },
    {
      type: "layer",
      geojson: *another geojson*,
      viewof: false
    }
  ]
})

Then

map

If you do not specify any particular layer, map will return the coordinates of the mouse cursor.

Update function

Since version 1.7 of bertin, an update function function is available. It allows you to modify specific attributes and styles without having to redraw the entire map. Not everything can be modified. Only the attributes underlined in the documentation are.

To use it, you must first create a map with the draw function add an identifier to each layer.

map = bertin.draw({
  layers: [
    {
      id: "mylayerid",
      geojson: data,
      fill: "red"
    }
  ]
})

Then you can apply the update function linked to the map to modify the styles and attributes. example

Code

map.update({
  id: "mylayerid",
  attr: "fill",
  value: "blue",
  duration: 1000
})

Parameters

Example 1

NB: The update function will also allow you to show and hide the layers of your map. See an example here.

Bonus: an overview of available layer types

What’s available in the bertin javascript library? What types of maps can be made? Thanks to this cheat sheet, you have an overview of the “types” available in the library. All these thumbnails are generated for real with the bertin\@1.5.9 in this notebook.