Visualizing commerce stream in Python maps — Half I: Bi-directional commerce stream maps | by Himalaya Bir Shrestha | Dec, 2023


Within the commerce stream maps, I aimed to characterize two-way commerce relationships between nations. For instance, the export from Nepal to India can be represented by the primary arrow (A1-A2) and the import by Nepal from India can be represented by a second arrow (A3-A4). On this means, every nation pair relationship would require 4 coordinate factors to outline the beginning and finish factors of arrows to characterize exports and imports respectively.

Whereas it is usually potential to imagine a coordinate that may be detected robotically (for instance, the centroid of a rustic geometry), I meant to mark the factors in a map and get their coordinates individually. For this function, it’s potential to create a venture in an utility comparable to Google Earth, export a KML file, and extract the coordinates with a converter (for instance, the GIS knowledge converter within the web site of MyGeodata Cloud).

Keyhole Markup Language (KML) is a file format used to show geographic knowledge in an utility comparable to Google Earth. It makes use of a tag-based construction with nested components and attributes and is predicated on the XML normal (Google, 2023).

The construction of my enter knowledge appears as proven within the picture beneath. It accommodates 5 completely different commerce relationships between neighboring nations: Nepal-India, Nepal-Bangladesh, Nepal-China, India-Pakistan, and India-Sri Lanka. For every nation pair, there are 4 coordinate factors for the beginning and finish factors of the 2 arrows. Value1 represents the export from Country1 to Country2. Value2 represents the import by Country1 from Country2. The purpose is to show this relationship in a Python map.

Information enter for commerce stream maps. Picture by Writer.

I learn the above knowledge as a pandas dataframe df. Moreover, I created dictionary objects comparable to transfers containing the export and import quantity between every nation pair, and startarrow1_dict containing the coordinate of place to begin of the primary arrow.

Creating mandatory dictionary objects. Picture by Writer.

On this part, I’ll describe the code used to visualise the commerce stream maps. I’ll primarily use the matplotlib and cartopy packages. I’ve additionally used the identical packages to visualise the worldwide floor temperature anomaly in one of my earlier posts.

  1. Import required packages

I began with importing the principle required packages and dependencies as proven beneath:

import cartopy.crs as ccrs
import cartopy.io.shapereader as shpreader

import matplotlib.pyplot as plt
import matplotlib.patches as mpatches
from matplotlib import colormaps
from matplotlib.colours import Normalize
from matplotlib.cm import ScalarMappable

import numpy as np
import pandas as pd
import os

2. Learn the form file

As a form file, I used the Pure Earth Vector. The vector file may be learn immediately by the shapereader module of the cartopy bundle.

# get the nation border file (10m decision) and extract
shpfilename = shpreader.natural_earth(
decision=”10m”,
class=”cultural”,
title=”admin_0_countries”,
)
reader = shpreader.Reader(shpfilename)
nations = reader.data()

Utilizing a bundle known as Fiona, it’s potential to learn the checklist of all nations as proven beneath.

Fiona bundle is used to open the form file and extract the checklist of all nation names. Picture by Writer.

3. Extract the data of solely required nations

Subsequent, I created required, which is an inventory of six nations having commerce relationships. I additionally created a dictionary object c, which contained the FionaRecord i.e., all related info of the nations that can be utilized for plotting.

# required nations
required = [“Nepal”, “India”, “Bangladesh”,”China”,”Pakistan”,”Sri Lanka”]

# extract the precise nation info
c = {
co.attributes["ADMIN"]: co
for co in nations if co.attributes["ADMIN"] in required
}

4. Plot the required nations and clipping

On this step, first, I plotted the geometries of required nations in a PlateCarree projection as proven beneath:

Plotting the required nations. Picture by Writer.

Subsequent, I needed to clip off the geometries of the remainder of the world in order that I may have a magnified view of the six nations alone. I made up my mind the extent of the utmost and minimal longitude and latitude values respectively that might cowl all six nations, set the extent for the axes plot, and plotted the nations. Within the for loop, I additionally added an code that might show the names of the nations over the centroid geometry of every nation.

The zorder attribute of the matplotlib bundle would decide the drawing order of the artists. Artists with larger zorder are drawn on the highest.

# get general boundary field from nation 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]]

ax = plt.axes(projection=ccrs.PlateCarree())

# get nation centroids
ax.set_extent([lon[0] - 1, lon[1] + 1, lat[0] - 1, lat[1] + 1])

for key, cn in zip(c.keys(),c.values()):
ax.add_geometries(cn.geometry,
crs=ccrs.PlateCarree(),
edgecolor="grey",
facecolor="whitesmoke",
zorder = 1)

# Add nation names
centroid = cn.geometry.centroid

ax.textual content(
centroid.x,
centroid.y,
key, # Assuming 'title' is the attribute containing the nation names
horizontalalignment='heart',
verticalalignment='heart',
rework=ccrs.PlateCarree(),
fontsize=8, # Modify the font dimension as wanted
colour='black', # Set the colour of the textual content
zorder = 2
)

plt.axis("off")
plt.present()

5. Arrange colormap, add arrow patches, and colour bar.

That is an important part of the code. First, I chosen viridis_r i.e., the reverse colour palette of viridis as my colormap. Subsequent, I made up my mind the minimal and most worth of any commerce values between nations as tmin and tmax respectively. These values are normalized such that the tmin corresponds to the bottom finish (0) and tmax corresponds to the best finish (1) of the colormap cmap and used accordingly within the succeeding code.

Then I looped by the transfers and used the FancyArrowPatch object to plot the arrows between nations. Every arrow object is related to a novel colour col that represents the commerce stream from one nation to a different. Whereas it is usually potential to make use of an offset from the coordinates of the primary arrow to plot the second arrow, I’ve specified the coordinates for the second arrow in my code. Within the code, the mutation_scale attribute is used to regulate the size of the top of the arrow, and the linewidth attribute is used to regulate the width of the principle line.

Lastly, I added the horizontal colorbar beneath the principle plot.

ax = plt.axes(projection=ccrs.PlateCarree())

# get nation centroids
ax.set_extent([lon[0] - 1, lon[1] + 1, lat[0] - 1, lat[1] + 1])

for key, cn in zip(c.keys(),c.values()):
ax.add_geometries(cn.geometry,
crs=ccrs.PlateCarree(),
edgecolor="gray",
facecolor="whitesmoke",
zorder = 1)

# Add nation names
centroid = cn.geometry.centroid

ax.textual content(
centroid.x,
centroid.y,
key, # Assuming 'title' is the attribute containing the nation names
horizontalalignment='heart',
verticalalignment='heart',
rework=ccrs.PlateCarree(),
fontsize=8, # Modify the font dimension as wanted
colour='black', # Set the colour of the textual content
zorder = 2
)

# arrange a colormap
cmap = colormaps.get("viridis_r")
tmin = np.array([v for v in transfers.values()]).min()
tmax = np.array([v for v in transfers.values()]).max()
norm = Normalize(tmin, tmax)

for tr in transfers:
c1, c2 = tr.break up(",")
startarrow1 = startarrow1_dict[tr]
endarrow1 = endarrow1_dict[tr]

startarrow2 = startarrow2_dict[tr]
endarrow2 = endarrow2_dict[tr]

t1 = transfers[tr][0]
col = cmap(norm(t1))

# Use the arrow operate to attract arrows
arrow = mpatches.FancyArrowPatch(
(startarrow1[0], startarrow1[1]),
(endarrow1[0], endarrow1[1]),
mutation_scale=20, #management the size of head of arrow
colour=col,
arrowstyle='-|>',
linewidth=2, # You possibly can regulate the linewidth to regulate the arrow physique width
zorder = 3
)
ax.add_patch(arrow)

#OTHER WAY
offset = 1
t2 = transfers[tr][1]
col = cmap(norm(t2))
arrow = mpatches.FancyArrowPatch(
(startarrow2[0], startarrow2[1]),
(endarrow2[0], endarrow2[1]),
mutation_scale=20,
colour=col,
arrowstyle='-|>',
linewidth=2, # You possibly can regulate the linewidth to regulate the arrow physique width
zorder = 4
)
ax.add_patch(arrow)

sm = ScalarMappable(norm, cmap)
fig = plt.gcf()
cbar = fig.colorbar(sm, ax=ax,
orientation = "horizontal",
pad = 0.05, #distance between important plot and colorbar
shrink = 0.8, #management size
side = 20 #management width
)
cbar.set_label("Commerce stream")

plt.title("Commerce stream in South Asia")
plt.axis("off")

plt.savefig("trade_flow2_with_labels.jpeg",
dpi = 300)
plt.present()

The top product is proven beneath. In my dummy dataset, the least commerce stream is export from Sri Lanka to India (53 items), which is represented by yellow colour. The best commerce stream is export from Bangladesh to Nepal (98 items), which is represented by violet colour.

Bi-directional commerce stream between nations represented by arrows between nations. Picture by Writer.

On this publish, I demonstrated how the commerce stream between nations together with export and import relationships may be visualized in a Python map utilizing two arrows. I’ve used the cartopy and matplotlib packages for this function. Within the second a part of this collection, I’ll showcase how the “web” commerce stream relationship may be visualized whereas highlighting the online exporter and web importer nations.

The pocket book for this publish is on the market on this GitHub repository. Thanks for studying!

References

Google Builders, 2023. KML Tutorial | Keyhole Markup Language | Google for Developers. The content material of this web page is licensed below the Creative Commons Attribution 4.0 License

Leave a Reply

Your email address will not be published. Required fields are marked *