Index de l'article

Work with shape files

Here we will use numpy and basemap.

pip install numpy
pip install basemap

To install basemap on WIndows, download a package here, according your versions, and install it with wheel.

Thanks to ramiro.org. I got the code below on his website, I just adapted it.

Suppose you have an Excel file with a country field, you get count and map them with a country shape. The country names in the Excel file and in the shape must be the same.

  1. import matplotlib as mpl
  2. import matplotlib.pyplot as plt
  3. import numpy as np
  4. import pandas as pd
  5.  
  6. from matplotlib.patches import Polygon
  7. from matplotlib.collections import PatchCollection
  8. from mpl_toolkits.basemap import Basemap
  9.  
  10. from PIL import Image, ImageOps
  11.  
  12. shp_simple_countries = r'C:/Users/Downloads/simple_countries/simple_countries'
  13.  
  14. inputExcelFile = r'C:/Users/Downloads/My file.xslx'
  15.  
  16. df = pd.read_excel(inputExcelFile, sheet_name='Export', engine='openpyxl', usecols=['ID', 'My Country Field'])
  17.  
  18. # Count the countries
  19. df_Country_count = pd.DataFrame(df.groupby(['My Country Field'], dropna=True).size(), columns=['Total']).sort_values(['Total'], ascending=False).reset_index()
  20. df_Country_count = df_Country_count.fillna('Unknow')
  21.  
  22. df_Country_count['Percent'] = (df_Country_count['Total'] / df_Country_count['Total'].sum()) * 100
  23. df_Country_count['Percent'] = df_Country_count['Percent'].round(decimals=2)
  24.  
  25. # Set countries as index
  26. df_Country_count.set_index('My Country Field', inplace=True)
  27.  
  28. my_values = df_Country_count['Percent']
  29.  
  30. num_colors = 30
  31. cm = plt.get_cmap('Blues')
  32.  
  33. scheme = [cm(i / num_colors) for i in range(num_colors)]
  34. my_range = np.linspace(my_values.min(), my_values.max(), num_colors)
  35.  
  36. # -1 TO AVOID SEARCHS IN A PANDAS DATA-FRAME INCLUDING START AND STOP VALUE (I think ...)
  37. df_Country_count['Percent'] = np.digitize(my_values, my_range) - 1
  38.  
  39. map1 = plt.figure(figsize=(14, 8))
  40. ax = map1.add_subplot(111, frame_on=False)
  41.  
  42. map1.suptitle('Countries', fontsize=30, y=.95)
  43.  
  44. m = Basemap(lon_0=0, projection='robin')
  45. m.drawmapboundary(color='w')
  46.  
  47. m.readshapefile(shp_simple_countries, 'units', color='#444444', linewidth=.2, default_encoding='iso-8859-15')
  48.  
  49. # Create the chloro map
  50. for info, shape in zip(m.units_info, m.units):
  51. shp_ctry = info['COUNTRY_HB']
  52. if shp_ctry not in df_Country_count.index:
  53. color = '#dddddd'
  54. else:
  55. color = scheme[df_Country_count.loc[shp_ctry]['Percent']]
  56.  
  57. patches = [Polygon(np.array(shape), True)]
  58. pc = PatchCollection(patches)
  59. pc.set_facecolor(color)
  60. ax.add_collection(pc)
  61.  
  62. # Cover up Antarctica so legend can be placed over it
  63. ax.axhspan(0, 1000 * 1800, facecolor='w', edgecolor='w', zorder=2)
  64.  
  65. # Draw color legend
  66. ax_legend = map1.add_axes([0.2, 0.14, 0.6, 0.03], zorder=3)
  67. cmap = mpl.colors.ListedColormap(scheme)
  68.  
  69. cb = mpl.colorbar.ColorbarBase(ax_legend, cmap=cmap, ticks=my_range, boundaries=my_range, orientation='horizontal')
  70. cb.ax.set_xticklabels([str(round(i, 1)) for i in my_range])
  71. cb.ax.tick_params(labelsize=7)
  72. cb.set_label('Percentage', rotation=0)
  73. #cb.remove()
  74.  
  75. # Set the map footer
  76. # description = 'Bla bla bla'
  77. # plt.annotate(description, xy=(-.8, -3.2), size=14, xycoords='axes fraction')
  78.  
  79. map1.savefig('C:/Users/Downloads/mymap1.png', dpi=100, bbox_inches='tight')
  80.  
  81. plt.show()
  82. plt.clf()
  83.  
  84. im = Image.open('C:/Users/Downloads/mymap1.png')
  85. bordered = ImageOps.expand(im, border=1, fill=(0, 0, 0))
  86. bordered.save('C:/Users/Downloads/mymap1.png')