In this tutorials, the following functions will be covered:
First, import things that is needed for the following steps.
In [1]:
import geopandas as gpd # for reading and manupulating shapefile
import matplotlib.pyplot as plt # for making figure
import colouringmap.mapping_polygon as mpoly # for making maps
# magic line for matlotlib figure to be shown inline in jupyter cell
%matplotlib inline
In [2]:
from palettable.colorbrewer.qualitative import Dark2_7 # to get the colormap for more custom manupulation in the last step
Second, read the file, and take a look on the attribute table of the shapefile.
In [3]:
grid_res = gpd.read_file('data/community_results.shp')
grid_res.head()
Out[3]:
Then, start playing with the shapefile. The following show how to prepare the figure before drawing the map on the figure.
The first line in the following cell is just a standard way to create a matplotlib figure, along with an "ax".
The second line prepare the "ax" for mapping things. Setting map_context to the context of the shapefile is to
make sure the shapefile is within the figure, so it will be shown within the figure.
The figure is just a matplotlib figure & ax, so you can also set the map context manually by something like this:
ax.set_xlim([minx, maxx])
ax.set_ylim([miny, maxy])
In [4]:
fig,ax = plt.subplots(figsize=(7,7))
ax = mpoly.prepare_map(ax, map_context=grid_res)
The following show how to create a map with just the shape of the polygons with a same colour.
In [5]:
fig,ax = plt.subplots(figsize=(7,7))
ax = mpoly.prepare_map(ax, map_context=grid_res)
ax = mpoly.map_shape(grid_res, ax, lw=.1, alpha=.7)
The above is all basic operations.
The following for mapping a column of a variable named "tweets". (actually this is the number of twitter tweets in the grid cell)
Next, lets try to map the 'tweets' column with default colormap.
use mpoly.map_sequence function, throw in the geopandas gdf, the column name, and the ax.
other settings just leave them out, use the default.
In [6]:
fig,ax = plt.subplots(figsize=(7,7))
ax = mpoly.prepare_map(ax, map_context=grid_res)
ax = mpoly.map_sequence(grid_res, 'tweets', ax)
The resulting map is not so good.
The mpoly.map_sequence need at least three parameter: gdf, a column name(numeric), and a ax. The other settings are optional, which default settings can be found in the following:
mpoly.map_sequence(gdf, colorbysequence, ax, break_method='quantile', break_N=6, break_cuts=[], break_vmin=None, break_vmax=None, color_group='cmocean_sequential', color_name='Turbid_10', reverse=False, lw=1., ec='#000000', alpha=1., zorder=1, extend_context=True, add_legend=True, font_path=None, legend_loc='upper left', legend_format='%.2f')
break them down:
The following show different types of break_method. To make the map better looking, the edge color (ec) is set to white, and the edge line-width (lw) is set to 0.2.
In [7]:
fig,ax = plt.subplots(figsize=(7,7))
ax = mpoly.prepare_map(ax, map_context=grid_res)
ax = mpoly.map_sequence(grid_res, 'tweets', ax, break_method='equal_interval', ec='w', lw=.2)
In [8]:
fig,ax = plt.subplots(figsize=(7,7))
ax = mpoly.prepare_map(ax, map_context=grid_res)
ax = mpoly.map_sequence(grid_res, 'tweets', ax, break_method='natural_break', ec='w', lw=.2)
In [9]:
fig,ax = plt.subplots(figsize=(7,7))
ax = mpoly.prepare_map(ax, map_context=grid_res)
ax = mpoly.map_sequence(grid_res, 'tweets', ax, break_method='head_tail_break', ec='w', lw=.2)
In [10]:
fig,ax = plt.subplots(figsize=(7,7))
ax = mpoly.prepare_map(ax, map_context=grid_res)
ax = mpoly.map_sequence(grid_res, 'tweets', ax, break_method='quantile', ec='w', lw=.2)
In [11]:
fig,ax = plt.subplots(figsize=(7,7))
ax = mpoly.prepare_map(ax, map_context=grid_res)
ax = mpoly.map_sequence(grid_res, 'tweets', ax, break_method='standard_deviation', ec='w', lw=.2) # experiment, advise not to use
the break_method include: natural_break, quantile, head_tail_break, standard_deviation. The result of standard_deviation may be weird if the vector does not follow a normal distribution.
The following use head_tail break, and increase the number of levels to 7.
In [12]:
fig,ax = plt.subplots(figsize=(7,7))
ax = mpoly.prepare_map(ax, map_context=grid_res)
ax = mpoly.map_sequence(grid_res, 'tweets', ax, break_method='head_tail_break', break_N=7, ec='w', lw=.2)
Lets' try some different colour.
colouringmap use palettable's colormap, so the available colormap in palettable should be useable in colouringmap.
In [13]:
fig,ax = plt.subplots(figsize=(7,7))
ax = mpoly.prepare_map(ax, map_context=grid_res)
ax = mpoly.map_sequence(grid_res, 'tweets', ax,
break_method='head_tail_break', break_N=7,
ec='w', lw=.2,
color_group='colorbrewer_sequential', color_name='YlOrRd_7')
In [14]:
fig,ax = plt.subplots(figsize=(7,7))
ax = mpoly.prepare_map(ax, map_context=grid_res)
ax = mpoly.map_sequence(grid_res, 'tweets', ax,
break_method='head_tail_break', break_N=7,
ec='w', lw=.2,
color_group='cmocean_sequential', color_name='Speed_7')
try the reverse colormap
In [15]:
fig,ax = plt.subplots(figsize=(7,7))
ax = mpoly.prepare_map(ax, map_context=grid_res)
ax = mpoly.map_sequence(grid_res, 'tweets', ax,
break_method='head_tail_break', break_N=7,
ec='w', lw=.2,
color_group='mycarta', color_name='CubeYF_7', reverse=True)
In [16]:
fig,ax = plt.subplots(figsize=(7,7))
ax = mpoly.prepare_map(ax, map_context=grid_res)
ax = mpoly.map_sequence(grid_res, 'tweets', ax,
break_method='head_tail_break', break_N=7,
ec='w', lw=.2,
color_group='mycarta', color_name='CubeYF_7', reverse=False)
In [20]:
fig,ax = plt.subplots(figsize=(7,7))
ax = mpoly.prepare_map(ax, map_context=grid_res)
ax = mpoly.map_sequence(grid_res, 'tweets', ax,
break_method='head_tail_break', break_N=7,
ec='w', lw=.2,
color_group='wesanderson', color_name='darjeeling3', reverse=True)
In [21]:
fig,ax = plt.subplots(figsize=(7,7))
ax = mpoly.prepare_map(ax, map_context=grid_res)
ax = mpoly.map_sequence(grid_res, 'tweets', ax,
break_method='head_tail_break', break_N=7,
ec='w', lw=.2,
color_group='matplotlib', color_name='Viridis_7', reverse=False)
The breaks result is usually complex, as they are calculated based on some rules. But sometimes, we want to make a map that the break values is simpler, such as round to hundreds, etc. So, after we tried some breaking methods, and found some nice breaking results, then we can manually round the numbers to the nearby hundreds values (or the ceiling or floor values).
The following use the manual break values method, with a parameter named "break_cuts" to make the breaks into some prefered break methods;
and change the location of the legend to the lower right
In [23]:
fig,ax = plt.subplots(figsize=(7,7))
ax = mpoly.prepare_map(ax, map_context=grid_res)
ax = mpoly.map_sequence(grid_res, 'tweets', ax,
break_method='manual', break_cuts=[400,1800,4500,8600,13700,15900],
ec='w', lw=.2,
color_group='matplotlib', color_name='Viridis_7',
legend_loc='lower right')
The legend in the previous map is overlapped on the map, so it is better to extend the map context to include more spaces.
In the following map, the figure size is larger, and the map context (xlim, ylim) is also increased. (just change them as a normal matplotlib ax element)
In [44]:
fig,ax = plt.subplots(figsize=(10,10))
ax = mpoly.prepare_map(ax, map_context=grid_res)
ax = mpoly.map_sequence(grid_res, 'tweets', ax,
break_method='manual', break_cuts=[400,1800,4500,8600,13700,15900],
ec='w', lw=.2,
color_group='matplotlib', color_name='Viridis_7',
legend_loc='lower right', extend_context=False)
x0,x1 = ax.get_xlim()
ax.set_xlim([x0-100, x1+3000])
y0,y1 = ax.get_ylim()
ax.set_ylim([y0-3000, y1+100])
#print x0,x1, y0,y1
The map looks better now.
But, it would be better if we also map the administrative boundaries to the map, to show which colour belong to which area. So lets add the boundary shapefile.
The following read the administrative boundary of the Tokyo 23 special wards.
In [46]:
borders = gpd.read_file('data/tokyo_special_ward.shp')
borders.head()
Out[46]:
Before we map the boundaries to the map, lets check if the two shapefile have the same projection.
In [47]:
print borders.crs==grid_res.crs # check if the two shapefile have the same projection
print borders.crs # check the two projections
print grid_res.crs
Turns out they are not same, so lets do some projection.
In [48]:
borders = borders.to_crs(grid_res.crs) # convert the borders projection to the same as the grid_res
print borders.crs==grid_res.crs # now check again if the two shapefile have the same projection
So they are now in the same projection.
Now, lets add the administrative boundaries to the map.
Because the boundaries are set to white colour (ec='w'), so the grid edge colour is changed to white (ec='w')
In [54]:
fig,ax = plt.subplots(figsize=(10,10))
ax = mpoly.prepare_map(ax, map_context=grid_res)
ax = mpoly.map_sequence(grid_res, 'tweets', ax,
break_method='manual', break_cuts=[400,1800,4500,8600,13700,15900],
ec='w', lw=.2,
color_group='matplotlib', color_name='Viridis_7',
legend_loc='lower right', extend_context=False)
ax = mpoly.add_border(borders, ax, ec='w', lw=2, alpha=.9)
x0,x1 = ax.get_xlim()
ax.set_xlim([x0-100, x1+3000])
y0,y1 = ax.get_ylim()
ax.set_ylim([y0-3000, y1+100])
#print x0,x1, y0,y1
Out[54]:
let try something else:
In [72]:
fig,ax = plt.subplots(figsize=(10,10))
ax = mpoly.prepare_map(ax, map_context=grid_res, background_colour='#3c4142')
ax = mpoly.map_sequence(grid_res, 'tweets', ax,
break_method='manual', break_cuts=[400,1800,4500,8600,13700,15900],
ec='w', lw=.0,
color_group='matplotlib', color_name='Viridis_7',
legend_loc='lower right', extend_context=False)
ax = mpoly.add_border(borders, ax, ec='w', lw=.5, alpha=.9)
x0,x1 = ax.get_xlim()
ax.set_xlim([x0-500, x1+3000])
y0,y1 = ax.get_ylim()
ax.set_ylim([y0-3000, y1+500])
#print x0,x1, y0,y1
Out[72]:
The following add a simple scalebar to the map, using the mpoly.add_scalebar() function.
In [74]:
fig,ax = plt.subplots(figsize=(10,10))
ax = mpoly.prepare_map(ax, map_context=grid_res, background_colour='#3c4142')
ax = mpoly.map_sequence(grid_res, 'tweets', ax,
break_method='manual', break_cuts=[400,1800,4500,8600,13700,15900],
ec='w', lw=.0,
color_group='matplotlib', color_name='Viridis_7',
legend_loc='lower right', extend_context=False)
ax = mpoly.add_border(borders, ax, ec='w', lw=.5, alpha=.9)
ax = mpoly.add_scalebar(ax)
x0,x1 = ax.get_xlim()
ax.set_xlim([x0-500, x1+3000])
y0,y1 = ax.get_ylim()
ax.set_ylim([y0-3000, y1+500])
#print x0,x1, y0,y1
Out[74]:
the scalebar is OK in most situation, but if you need to make more customization, please use the matplotlib_scalebar directly (which is used by the colouringmap).
The matplotlib-scalebar url: https://pypi.python.org/pypi/matplotlib-scalebar
The following show how to customize the scalebar directly using matplotlib-scalebar.
note that the first value of the Scalebar is 1, meaning 1 pixel = 1 meter.
In [75]:
from matplotlib_scalebar.scalebar import ScaleBar
In [83]:
fig,ax = plt.subplots(figsize=(10,10))
ax = mpoly.prepare_map(ax, map_context=grid_res, background_colour='#3c4142')
ax = mpoly.map_sequence(grid_res, 'tweets', ax,
break_method='manual', break_cuts=[400,1800,4500,8600,13700,15900],
ec='w', lw=.0,
color_group='matplotlib', color_name='Viridis_7',
legend_loc='lower right', extend_context=False)
ax = mpoly.add_border(borders, ax, ec='w', lw=.5, alpha=.9)
#ax = mpoly.add_scalebar(ax)
scalebar = ScaleBar(1, fixed_value=10, fixed_units='km',location='lower left', color='blue') # 1 pixel = 1 meter
ax.add_artist(scalebar)
x0,x1 = ax.get_xlim()
ax.set_xlim([x0-500, x1+3000])
y0,y1 = ax.get_ylim()
ax.set_ylim([y0-3000, y1+500])
#print x0,x1, y0,y1
Out[83]:
I think the result now is quite nice~ (for the tutorials)
It can be better if you give the settings of the colour groups/colour name / bacgkround colour / lines ... more tries, and find the prefered designset of yours.
That is all in this tutorial.
In [ ]: