Issue
I want to show trade flow between countries in a map. For this, I want to add 1 or 2 arrows to and from country as shown below. The arrow should have color gradients, which represent certain numeric values.
1.How can I add arrows on top of countries in maps using geopandas or other packages?
- How do I add color gradients to those arrows using color maps? I also need the legend for the color values.
Solution
Here's a basic implementation using cartopy (and based on, e.g., Cartopy - multiple arrows using annotate):
import numpy as np
from matplotlib import pyplot as plt
from matplotlib import colormaps
from matplotlib.colors import Normalize
from matplotlib.cm import ScalarMappable
import cartopy.io.shapereader as shpreader
import cartopy.crs as ccrs
# required countries
required = ["Egypt", "Sudan", "South Sudan"]
# get the country border file (10m resolution) and extract
shpfilename = shpreader.natural_earth(
resolution="10m",
category="cultural",
name="admin_0_countries",
)
reader = shpreader.Reader(shpfilename)
countries = reader.records()
# extract the specific country information
c = {
co.attributes["ADMIN"]: co
for co in countries if co.attributes["ADMIN"] in required
}
# get overall boundary box from country bounds
extents = np.array([c[cn].bounds for cn in c])
lon = [extents.min(0)[0], extents.max(0)[2]]
lat = [extents.min(0)[1], extents.max(0)[3]]
# get country centroids
centroids = {
cn: [c[cn].geometry.centroid.x, c[cn].geometry.centroid.y] for cn in c
}
# plot the countries
ax = plt.axes(projection=ccrs.PlateCarree())
for cn in c.values():
ax.add_geometries(cn.geometry, crs=ccrs.PlateCarree(), edgecolor="white", facecolor="lightgray")
ax.set_extent([lon[0] - 1, lon[1] + 1, lat[0] - 1, lat[1] + 1])
# flows in and out of country pairs
transfers = {"Egypt,Sudan": [2.3, 8.9], "Sudan,South Sudan": [6.5, 0.9]}
# set up a colormap
cmap = colormaps.get_cmap("Greens")
tmax = np.array([v for v in transfers.values()]).max()
tmin = np.array([v for v in transfers.values()]).min()
norm = Normalize(tmin, tmax)
offset = 1.0
for tr in transfers:
c1, c2 = tr.split(",")
cent1 = centroids[c1]
cent2 = centroids[c2]
# one way
t1 = transfers[tr][0]
col = cmap(norm(t1))
ax.annotate(
"",
xy=(cent2[0] - offset, cent2[1] - offset),
xytext=(cent1[0] - offset, cent1[1] - offset),
xycoords="data",
arrowprops={"facecolor": col},
)
# other way
t2 = transfers[tr][1]
col = cmap(norm(t2))
ax.annotate(
"",
xy=(cent1[0] + offset, cent1[1] + offset),
xytext=(cent2[0] + offset, cent2[1] + offset),
xycoords="data",
arrowprops={"facecolor": col},
)
# set the colorbar
sm = ScalarMappable(norm, cmap)
fig = plt.gcf()
fig.colorbar(sm, ax=ax)
plt.show()
which produces:
You'll likely want to play about with the actual positioning of the arrow start and end points.
Answered By - Matt Pitkin
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.