The visualization of thematic maps can get very messy very quick when there are many points to plot display.
In this post we will plot data from shapefile
in the most visually efficient way possible.
Let's open our shapefiles with geopandas
. (GeoPandas makes our task easy and that will be clear in a moment.)
import geopandas
path = './data/energy/{}'.format
hidro = geopandas.GeoDataFrame.from_file(path('ENC_Hidreletrica_P.shp'))
termo = geopandas.GeoDataFrame.from_file(path('ENC_Termeletrica_P.shp'))
We need to know the Coordinate Reference System (CRS) of the data.
That information is stored in the .crs
property and we can use pyepsg to get more information about it.
import pyepsg
pyepsg.get(hidro.crs['init'].split(':')[1])
SIRGAS 2000 is a common CRS used both onshore and offshore data in Central America and South America. However, to plot the data on a folium map, we need to convert to a Geographic coordinate system with the wgs84 datum (EPSG: 4326). We also need to greate a GeoJSON object out of the GeoDataFrame.
Luckily, geopandas
makes that extremely easy with the to_crs()
method and, chained with the to_json()
, we have an object ready for plotting with just one line.
gjson = hidro.to_crs(epsg='4326').to_json()
import folium
mapa = folium.Map([-15.783333, -47.866667],
zoom_start=4,
tiles='cartodbpositron')
points = folium.features.GeoJson(gjson)
mapa.add_children(points)
mapa
It is very hard to make sense of what we are plotting. The information is cluttered and has no description.
To make it better we can add more information as rich HTML popups.
And to reduced the clutter we can use the plugin MarkerCluster
.
Since we have to groups of data to plot we can also use FeatureGroup
to turn on/off the display of a specific group.
table = """
<!DOCTYPE html>
<html>
<head>
<style>
table {{
width:100%;
}}
table, th, td {{
border: 1px solid black;
border-collapse: collapse;
}}
th, td {{
padding: 5px;
text-align: left;
}}
table#t01 tr:nth-child(odd) {{
background-color: #eee;
}}
table#t01 tr:nth-child(even) {{
background-color:#fff;
}}
</style>
</head>
<body>
<table id="t01">
<tr>
<td>Type</td>
<td>{}</td>
</tr>
<tr>
<td>Name</td>
<td>{}</td>
</tr>
<tr>
<td>Operational</td>
<td>{}</td>
</tr>
</table>
</body>
</html>
""".format
mapa = folium.Map([-15.783333, -47.866667],
zoom_start=4,
tiles='cartodbpositron')
from folium.element import IFrame
from folium.plugins import MarkerCluster
width, height = 310,110
popups, locations = [], []
for idx, row in hidro.iterrows():
locations.append([row['geometry'].y, row['geometry'].x])
name = row['NOME'].encode('ascii', 'xmlcharrefreplace')
if row['OPERACIONA'] == 'Sim':
opr = 'Yes'
elif row['OPERACIONA'] == u'Não':
opr = 'No'
else:
opr = 'NA'
iframe = IFrame(table('Hydroelectric', name, opr), width=width, height=height)
popups.append(iframe)
h = folium.FeatureGroup(name='Hydroelectric')
h.add_children(MarkerCluster(locations=locations, popups=popups))
mapa.add_children(h)
popups, locations = [], []
for idx, row in termo.iterrows():
locations.append([row['geometry'].y, row['geometry'].x])
name = row['NOME'].encode('ascii', 'xmlcharrefreplace')
if row['OPERACIONA'] == 'Sim':
opr = 'Yes'
elif row['OPERACIONA'] == u'Não':
opr = 'No'
else:
opr = 'NA'
iframe = IFrame(table('Thermoelectric', name, opr), width=width, height=height)
popups.append(iframe)
t = folium.FeatureGroup(name='Thermoelectric')
t.add_children(MarkerCluster(locations=locations, popups=popups))
mapa.add_children(t)
mapa.add_children(folium.LayerControl())
mapa
Much better! We can choose the context of what we want to display (Thermoelectric or Hydroelectric data "layer") and the points are aggregated by proximity thanks to MarkerCluster
.
That is all for today! Enjoy data ;-)
HTML(html)