Python – Map Visualization with Basemap
Many datasets have location information – longitudes and latitudes. Census data, traffic data, landmarks data, etc. If we have data associated with locations, it may be very useful to display the locations on a map. This would not only allow us to visualize the locations of interest, but also follow trends and draw insights.
Python makes it pretty easy to create attractive and informative. The main tool that I am using here for map visualization is Matplotlib’s Basemap.
I go through a few examples (there are lots of Basemap examples out in the wild, but still). As it’s not a tutorial, I don’t go over the installations and detail explanations of the packages. There are a few links at the end of this post that you might find useful to familiarize yourself with the tools and packages I use, or to give yourself a refresher.
Drawing a Basic Map
First, we need to decide on a rectangular region of the World that we want to appear on the map. We need longitude and latitude specifying the north-south-east-west boundaries.
This is a handy website that allows us to select a rectangular bounding box – the region of interest. We can navigate in the World map, and select the ‘rectangular box’ icon and draw a bounding box. We can output the the north-south-east-west boundaries of the selected region. There are a few options for the output such as GeoJson, csv, DublinCore, etc. The DublinCore output is very intuitive.
Example: Output of DublinCore for the selected region.
The lower left corner of the bounding box is defined by westlimit and southlimit, and the upper right corner by eastlimit and northlimit.
This is the information we need to start a map.
The following code block starts the map drawing process. The
basemap command creates a basemap object with the necessary arguments.
m = Basemap(resolution ='l', projection ='merc', lat_0= lat_midpoint, lon_0= lon_midpoint, llcrnrlon = westlimit, llcrnrlat = southlimit, urcrnrlon = eastlimit, urcrnrlat = northlimit)
Let me go through the arguments.
resolution: the quality of the map. The higher the resolution the longer it takes to render the map. It may be a good idea to use a lower resolution when working on the map, and set it to a higher resolution before reporting.
crude (c), low (l), intermediate (i), high (h), full (f).
projection: In order to represent the curved surface of the earth on a two-dimensional map, a map projection is needed.
projectionselects display surface of the map: flat, curved, warpped, etc.
Options: Check out the Basemap documentation for available options. It’s hard to explain in words. Click on the projection examples in the documentation, and I hope you agree with me that a picture is worth a thousand words.
lon_0: latitude and longitude of the centre point of the map. I have set it to be the mid point of the bounding box. I compute it from the abounding box limits.
lon_midpoint = (westlimit+eastlimit)/2 lat_midpoint = (northlimit+southlimit)/2
urcrnrlat: bounding box corners (e.g.
llcrnrlonstands for lower left corner longitude).
Now we are ready to add land covers on the map. Like the projection of the map, there are lots of geographic components we can display - coastlines, rivers and political boundaries, etc. I recommend you take a look at the Basemap documentation for available options.
I draw a very few. This is just a proof of concept, so I kept it simple.
Here’s the hexadecimal colors I define for different components. It’s convenient to define the colors of different map components. We can make changes easily.
canvas = '#29293d' coastline = '#000000' land = '#669999' river = '#ccffff' lake = '#66ffff' stateboundary = '#ff884d'
First, I draw an empty map specifying a fill color and a thick boundary. Next, I add coastlines, land, water body and state boundaries. The commands and arguments are self explanatory.
m.drawmapboundary(fill_color = canvas, linewidth = 3)
m.drawcoastlines(linewidth=1.0, linestyle='solid', color= coastline)
Land and main water body
m.fillcontinents(color= land, lake_color= lake)
m.drawstates(linewidth=2, linestyle='solid', color= stateboundary)
m.drawrivers(linewidth=1, linestyle='solid', color= river)
As mentioned, this is a very basic map. I can and should make it prettier.