🚀 From 0 to EOmaps - a quickstart guide

The following section is intended to provide a quick overview on how to get started using EOmaps.

The guide is intended to be concise and covers only the most important information to get things running. Links to websites that provide additional information are provided throughout the text.


This tutorial is intended to get you up and running from scratch as fast as possible using 100% free and open-source tools. It does not cover alternative ways on how to setup python or how to use alternative editors etc.

Feel free to start a discussion of open an issue if you have additional questions or suggestions!


  • A computer (Windows / MacOS / Linux) - no root access required

  • basics on how to work with a terminal

  • basic python knowledge

🐍 Getting started - set up a python environment

There are of course many ways to set up a python environment… in the following, we will use the (free and open-source) conda package manager which greatly simplifies the creation of environments that depend on both python and c++ libraries (A lot of python-packages for geo-libraries are just bindings to c++ libraries such as GDAL or PyProj).

conda is available for MacOS, Windows and Linux. For more details, see conda-docs .

The fastest way to get started is to use miniconda, a minimalistic installer that includes only conda, python and a minimal set of additional packages.

TODO (~10 min)

Download and install the latest version of miniconda from:


After the installation is finished, open a Anaconda Prompt terminal (if you need help, see “starting conda” ).

By default, the terminal starts in the conda-environment base (you should see the name in brackets on the left).

Before creating a new dedicated python-environment, we’ll first install mamba into the base environment to speedup the installation process. (for details on mamba, have a look at the mamba-docs !)

  • NOTE: The use of mamba is optional (but highly recommended)! If you don’t want to use it, just replace the term mamba with conda in all subsequent commands.

TODO (~1 min) install mamba:

Install mamba from the conda-forge channel with the following command (in the Anaconda Prompt terminal):

conda install -c conda-forge mamba

Now that mamba is installed, we can use it to create a new python environment, and install the following packages:

  • As editor to write and execute python-code we will use the awesome (free and open-source) spyder-IDE

  • in addition, we install eomaps and matplotlib (the latter is only required to make sure matplotlib backends such as Qt5Agg are installed)

    • all required dependencies will be automatically determined and installed during the process

TODO (~15 min, ~500mb) setup environment:

Setup a new python environment named eomaps and install EOmaps and the spyder IDE.

mamba create -n eomaps -c conda-forge spyder eomaps matplotlib

Once the list of required packages is determined, confirm the installation with y and wait until it is completed.

Finally, all that’s left to do is to activate the environment and start the editor!

TODO (~1 min) activate environment, start and setup the spyder IDE:

To activate the environment, use:

conda activate eomaps

After activating the environment, we can start the spyder IDE with:


As a last step, we need to adjust the default plot-settings of the spyder IDE to make sure that matplotlib plots are generated as interactive widgets

  • By default, plots are rendered as static images into the “plots-pane”… to avoid this and create interactive matplotlib widgets instead, go to the preferences, select the IPython console section and set the Graphics backend to Automatic :


Now you’re ready for your first map! -> head over to 🗺 EOmaps examples and run one of the example-codes!

A few quick spyder IDE tips:

In spyder you will work with an interactive IPython console. This allows you to dynamically execute parts of a script and immediately see the outcome.

  • use F9 to execute the selected code (or the current line if nothing is selected)

  • use F5 to execute the current script (e.g. the whole file)

  • use shift + enter to execute the currently selected code-block (code-blocks are separated by # %%)

Configuring the editor (IDE)

Spyder IDE

To use the whole potential of EOmaps with the awesome Spyder IDE , the plot-settings must be adjusted to ensure that matplotlib plots remain interactive.

  • By default, plots are rendered as static images into the “plots-pane”… to avoid this and create interactive matplotlib widgets instead, go to the preferences and set the “Graphics backend” to “Automatic” :


PyCharm IDE

The PyCharm IDE automatically registers its own matplotlib backend which (for some unknown reason) freezes on interactive plots.

To my knowledge there are 2 possibilities to force pycharm to use the original matplotlib backends:

  • 🚲 The “manual” way:
    Add the following lines to the start of each script:
    (for more info and alternative backends see matplotlib docs)
    import matplotlib
  • 🚗 The “automatic” way:
    Go to the preferences and add the aforementioned lines to the “Starting script”
    (to ensure that the matplotlib backend is always set prior to running a script)

In addition, if you use a commercial version of PyCharm, make sure to disable “Show plots in tool window” in the Python Scientific preferences since it forces plots to be rendered as static images.


Jupyter Notebooks

To get the most out of EOmaps in Jupyter Notebooks, use jupyter lab together with the ipympl (widget) backend.

  • To install, use conda install -c conda-forge ipympl

Once it’s installed, use the command %matplotlib widget at the start of the code to activate the backend.

Using the Companion Widget

To use the 🧰 Companion Widget in backends other than Qt the Qt event-loop must be integrated. This can be done with the %gui qt command.

%matplotlib widget
%gui qt

from eomaps import Maps
m = Maps()
m.add_feature.preset("coastline", "ocean")

Alternative backends:

For classical notebooks, there’s also the nbagg backend provided by matplotlib

  • To use it, simply execute the magic %matplotlib notebook before starting to plot.

And you can also use the magic %matplotlib qt to use the default qt5agg backend.

  • This way the plots will NOT be embedded in the notebook, they show up as popups.


It is possible to plot static snapshots of the current state of a map to a Jupyter Notebook irrespective of the used backend by using m.snapshot(), e.g.:

m = Maps()

Checkout the matplotlib doc for more info!

Record interactive maps to create animations

The best way to record interactions on a EOmaps map is with the free and open source ScreenToGif software.

All animated gifs in this documentation have been created with this awesome piece of software.

Important changes between major versions

⚙ From EOmaps v3.x to v4.x

Changes between EOmaps v3.x and EOmaps v4.0:

  • the following properties and functions have been removed:

    • m.plot_specs.

    • m.set_plot_specs()

    • arguments are now directly passed to relevant functions:
      m.plot_map(), m.add_colorbar() and m.set_data()
  • 🔶 m.set_shape.voroni_diagram() is renamed to m.set_shape.voronoi_diagram()

  • 🔷 custom callbacks are no longer bound to the Maps-object
    the call-signature of custom callbacks has changed to:
    def cb(self, *args, **kwargs) >> def cb(*args, **kwargs)

Porting a script from v3.x to v4.x is quick and easy and involves the following steps:

  1. Search your script for all occurrences of the words .plot_specs and .set_plot_specs(, move the affected arguments to the correct functions (and remove the calls once you’re done):

    • vmin, vmax alpha and cmap are now set in
      m.plot_map(vmin=..., vmax=..., alpha=..., cmap=...)
    • histbins, label, tick_precision and density are now set in
      m.add_colorbar(histbins=..., label=..., tick_precision=..., density=...)
    • cpos and cpos_radius are now (optionally) set in
      m.set_data(data, x, y, cpos=..., cpos_radius=...)
  2. Search your script for all occurrences of the words xcoord and ycoord and replace them with x and y

  3. ONLY if you used voronoi diagrams:

  • search in your script for all occurrences of the word voroni_diagram and replace it with voronoi_diagram

  1. ONLY if you used custom callback functions:

  • the first argument of custom callbacks is no longer identified as the Maps object.

  • if you really need access to the Maps object within the callback, pass it as an explicit argument!

EOmaps v3.x:

m = Maps()
m.set_data(data=..., xcoord=..., ycoord=...)
m.set_plot_specs(vmin=1, vmax=20, cmap="viridis", histbins=100, cpos="ul", cpos_radius=1)

# ---------------------------- custom callback signature:
def custom_cb(m, asdf=1):


EOmaps v4.x:

m = Maps()
m.set_data(data=..., x=..., y=..., cpos="ul", cpos_radius=1)
m.plot_map(vmin=1, vmax=20, cmap="viridis")

# ---------------------------- custom callback signature:
def custom_cb(**kwargs, asdf=None):

m.cb.click.attach(custom_cb, asdf=1)

Note: if you really need access to the maps-object within custom callbacks, simply provide it as an explicit argument!

def custom_cb(**kwargs, m=None, asdf=None):

m.cb.click.attach(custom_cb, m=m, asdf=1)

⚙ From EOmaps v5.x to v6.x

General changes in behavior

  • 🔶 Starting with EOmaps v6.0 multiple calls to m.plot_map()
    on the same Maps-object completely remove (and replace) the previous dataset!
    (use a new Maps-object on the same layer for datasets that should be visible at the same time!)
  • 🔶 WebMap services are no longer re-fetched by default when exporting images with m.savefig()
    To force a re-fetch of WebMap services prior to saving the image at the desired dpi, use m.savefig(refetch_wms=True)
    (see m.refetch_wms_on_size_change() for more details)
  • 🔷 m.add_gdf now uses only valid geometries
    (to revert to the old behavior, use: m.add_gdf(..., only_valid=False))
  • 🔷 the order at which multi-layers are combined now determines the stacking of the artists

    • m.show_layer("A|B") plots all artists of the layer "A" on top of the layer "B"

    • the ordering of artists inside a layer is determined by their zorder (e.g. m.plot_map(zorder=123))

Removed (previously depreciated) functionalities

  • ❌ the m.figure accessor has been removed!

    • Use m.ax, m.f, m.colorbar.ax_cb, m.colorbar.ax_cb_plot instead

  • ❌ kwargs for m.plot_map(...)

    • "coastlines" use m.add_feature.preset.coastline() instead

  • ❌ kwargs for m.set_data(...)

    • "in_crs" use "crs" instead

    • "xcoord" use "x" instead

    • "ycoord" use "y" instead

  • ❌ kwargs for Maps(...)

    • "parent" … no longer needed

    • "gs_ax" use "ax" instead

  • ❌ kwargs for m.new_inset_maps(...)

    • "edgecolor" and "facecolor" use boundary=dict(ec=..., fc=...) instead

  • ❌ kwargs for m.add_colorbar(...)

    • "histbins" use "hist_bins" instead

    • "histogram_size" use "hist_size" instead

    • "density" use "hist_kwargs=dict(density=...)" instead

    • "top", "bottom", "left", "right" use margin=dict(top=..., bottom=..., left=..., right=...) instead

    • "add_extend_arrows"

  • m.indicate_masked_points() has been removed, use m.plot_map(indicate_masked_points=True) instead

  • m.shape.get_transformer is now a private (e.g. m.shape._get_transformer)

  • m.shape.radius_estimation_range is now a private (e.g. m.shape._radius_estimation_range)

⚙ From EOmaps v6.x to v7.x

  • ⚠️ A lot of internal functions and classes have been re-named to better follow PEP8 naming conventions. While this should not interfere with the public API, more extensive customizations might need to be adjusted with respect to the new names.

    If you encounter any problems, feel free to open an issue , and I’ll see what I can do!

    • For example: the module _shapes.py is now called shapes.py and the class shapes is now called Shapes

  • ⚠️ The use of m.set_data_specs(...) is depreciated. Use m.set_data(...) instead!

  • Figure export routines have been completely re-worked (but should result in the exact same output as in v6.x)