Country labels on Basemap

4.3k views Asked by At

I would like to plot a trajectory on a Basemap, and have country labels (names) shown as an overlay.

Here is the current code and the map it produces:

import pandas as pd 
import matplotlib.pyplot as plt 
from mpl_toolkits.basemap import Basemap


path = "path\\to\\data"

animal_data = pd.DataFrame.from_csv(path, header=None)
animal_data.columns = ["date", "time", "gps_lat", "gps_long"]

# data cleaning omitted for clarity 

params = {
        'projection':'merc', 
        'lat_0':animal_data.gps_lat.mean(), 
        'lon_0':animal_data.gps_long.mean(), 
        'resolution':'h', 
        'area_thresh':0.1, 
        'llcrnrlon':animal_data.gps_long.min()-10, 
        'llcrnrlat':animal_data.gps_lat.min()-10, 
        'urcrnrlon':animal_data.gps_long.max()+10, 
        'urcrnrlat':animal_data.gps_lat.max()+10
}
map = Basemap(**params)

map.drawcoastlines()
map.drawcountries()
map.fillcontinents(color = 'coral')
map.drawmapboundary()          

x, y = map(animal_data.gps_long.values, animal_data.gps_lat.values)

map.plot(x, y, 'b-', linewidth=1)    
plt.show()

This results in the map: Migration

This is a map of the trajectory of a migrating bird. While this is a very nice map (!), I need country-name labels so it is easy to determine the countries the bird is flying through.

Is there a straight-forward way of adding the country names?

1

There are 1 answers

0
Hezi Resheff On

My solution relies on an external data file that may or may not be available in the future. However, similar data can be found elsewhere, so that should not be too much of a problem.

First, the code for printing the country-name labels:

import pandas as pd 
import matplotlib.pyplot as plt 
from mpl_toolkits.basemap import Basemap

class MyBasemap(Basemap):     
    def printcountries(self, d=3, max_len=12):
        data = pd.io.parsers.read_csv("http://opengeocode.org/cude/download.php?file=/home/fashions/public_html/opengeocode.org/download/cow.txt", 
                                      sep=";", skiprows=28 )
        data = data[(data.latitude > self.llcrnrlat+d) & (data.latitude < self.urcrnrlat-d) & (data.longitude > self.llcrnrlon+d) & (data.longitude < self.urcrnrlon-d)]
        for ix, country in data.iterrows():                            
                plt.text(*self(country.longitude, country.latitude), s=country.BGN_name[:max_len]) 

All this does is to download a country-location database from here, then select countries that are currently on the map, and label them.

The complete code:

import pandas as pd 
import matplotlib.pyplot as plt 
from mpl_toolkits.basemap import Basemap

class MyBasemap(Basemap):     
    def printcountries(self, d=3, max_len=12):
        data = pd.io.parsers.read_csv("http://opengeocode.org/cude/download.php?file=/home/fashions/public_html/opengeocode.org/download/cow.txt", 
                                      sep=";", skiprows=28 )
        data = data[(data.latitude > self.llcrnrlat+d) & (data.latitude < self.urcrnrlat-d) & (data.longitude > self.llcrnrlon+d) & (data.longitude < self.urcrnrlon-d)]
        for ix, country in data.iterrows():                            
                plt.text(*self(country.longitude, country.latitude), s=country.BGN_name[:max_len]) 


path = "path\\to\\data"

animal_data = pd.DataFrame.from_csv(path, header=None)
animal_data.columns = ["date", "time", "gps_lat", "gps_long"]

params = {
        'projection':'merc', 
        'lat_0':animal_data.gps_lat.mean(), 
        'lon_0':animal_data.gps_long.mean(), 
        'resolution':'h', 
        'area_thresh':0.1, 
        'llcrnrlon':animal_data.gps_long.min()-10, 
        'llcrnrlat':animal_data.gps_lat.min()-10, 
        'urcrnrlon':animal_data.gps_long.max()+10, 
        'urcrnrlat':animal_data.gps_lat.max()+10
}

plt.figure()
map = MyBasemap(**params)

map.drawcoastlines()
map.fillcontinents(color = 'coral')
map.drawmapboundary()          
map.drawcountries()
map.printcountries()

x, y = map(animal_data.gps_long.values, animal_data.gps_lat.values)

map.plot(x, y, 'b-', linewidth=1)    
plt.show()

and finally, the result:

labeld-map

Clearly this isn't as carefully labeled as one might hope, and some heuristics regarding country size, name length and map size should be implemented to make this perfect, but this is a good starting point.