Search code examples
pythonmatplotlibnetworkxz-ordercartopy

cartopy + networkx: zorder is not working


I need to generate a map using cartopy and plot some data over it (using networkx). I am able to do it, but networkx objects are behind the map. I am trying to force the order of the layers using zorder but... it is not working :(

My only idea is to add some transparency to the cartopy geometries, but it does not look nice at all... (in this example it does not look so bad, but with my whole data, it looks terrible)

Any ideias on how to force the order?

Here is my code:

import cartopy.crs as ccrs
from cartopy.io import shapereader as shpreader
import matplotlib.pyplot as plt
import networkx as nx

paises = ['Portugal', 'France', 'Canada', 'Brazil', 'Kazakhstan']
cidades = ['Aveiro', 'Ust-Kamenogorsk', 'Manaus']
links = [('Aveiro', 'Ust-Kamenogorsk'), 
         ('Manaus', 'Ust-Kamenogorsk'),
         ('Aveiro', 'Manaus')]
position = {'Aveiro': (-8.65, 40.6),
            'Manaus': (-60.0, -3.1),
            'Ust-Kamenogorsk': (82.6, 49.97)}

# map using cartopy:
shapename = 'admin_0_countries'
countries_shp = shpreader.natural_earth(resolution='110m',
                                    category='cultural', name=shapename)

ax = plt.axes(projection=ccrs.PlateCarree(central_longitude=0.0, globe=None))
ax.set_global()

for country in shpreader.Reader(countries_shp).records():    
    nome = country.attributes['name_long']
    if nome in paises:
        i = paises.index(nome)
        artist = ax.add_geometries(country.geometry, ccrs.PlateCarree(),
                           facecolor='yellow',
                           #alpha=0.5,
                           zorder=10)
    else:
        artist = ax.add_geometries(country.geometry, ccrs.PlateCarree(),
                           facecolor='0.9',
                           zorder=10)

# add some data over the cartopy map (using networkx):                              
G = nx.Graph()
G.add_nodes_from(cidades)
G.add_edges_from(links)

nx.draw_networkx_nodes(G, position, node_size=20, nodelist=cidades, zorder=20)
edges=nx.draw_networkx_edges(G, position, edgelist=links, zorder=20)

plt.show()

And this is the image I get: zorder not working with networkx over cartopy


Solution

  • What happened is that your zorder=20 doesn't do anything; it got ignored as you can see in their source code. What networkx do in their draw_networkx_edges code is:

    def draw_networkx_edges(G, pos,
        ...
        edge_collection.set_zorder(1)  # edges go behind nodes
        ...
    

    in their draw_networkx_nodes code is:

    def draw_networkx_nodes(G, pos,
        ...
        node_collection.set_zorder(2)
        ...
    

    Now, the solution is straightforward:

    • If you set zorder in add_geometries to 1, nodes will then be in front of the map because it's zorder 2. But edges are still behind the map because it's zorder 1.
    • Now the real better solution is to get both node_collection and edge_collection first:

      nodes = nx.draw_networkx_nodes(G, position, node_size=20, nodelist=cidades)
      edges = nx.draw_networkx_edges(G, position, edgelist=links)
      

      Then set_zorder for both nodes and edges:

      nodes.set_zorder(20)
      edges.set_zorder(20)