How to add text to folium choropleth maps - folium

I am using sample data from geopandas for this example. But basically what I want to do is have a choropleth map with text displayed on it by default.
Currently, I have used tooltip='BoroCode' to display the text I need however, I don't want the text displayed only when you hover over the area. I'd like it to be displayed all the time.
import folium
import branca.colormap as cmp
import geopandas
path_to_data = geopandas.datasets.get_path("nybb")
gdf = geopandas.read_file(path_to_data)
# create map
m= folium.Map(location=[40.730610, -73.935242])
# add boroughs
gdf.explore(
m=m,
column="BoroName",
scheme="naturalbreaks",
legend=True,
k=10,
cmap='winter',
legend_kwds=dict(),
name="boroughs" ,
tooltip='BoroCode'
)
folium.LayerControl().add_to(m)
m

I don't have enough experience with geopandas, but in this case, gdf.explore() doesn't seem to have the ability to add annotations, so you can add a marker with html format text set with an icon on the folium side. If the map coordinate system in geopandas is The map coordinate system of geopandas is not in a format that can be used by folium, so it is converted. Then, the center point of the borough is obtained. A warning will be displayed if this center point is not correct. I think the solution to avoid this warning is to use the actual center point.
import folium
from folium.features import DivIcon
import branca.colormap as cmp
import geopandas
path_to_data = geopandas.datasets.get_path("nybb")
gdf = geopandas.read_file(path_to_data)
gdf = gdf.to_crs(epsg=4326)
gdf['centroid'] = gdf.centroid
gdf['lat'] = gdf['centroid'].map(lambda p: p.y)
gdf['lon'] = gdf['centroid'].map(lambda p: p.x)
m = folium.Map(location=[40.730610, -73.935242])
for i, row in gdf.iterrows():
folium.map.Marker(
[row['lat'],row['lon']],
icon=DivIcon(
icon_size=(100,24),
icon_anchor=(0,0),
html=f'<div style="font-size:16px; color:white;">{row["BoroName"]}</div>',
)
).add_to(m)
#add boroughs
gdf.explore(
m=m,
column="BoroName",
scheme="naturalbreaks",
legend=True,
k=10,
cmap='winter',
legend_kwds=dict(),
name="boroughs",
tooltip='BoroName'
)
folium.LayerControl().add_to(m)
m

Related

Drawing a community in networkx, anything I am doing incorrectly?

Trying to do something like this but I am not sure what I am doing incorrectly
import networkx as nx
import matplotlib.pyplot as plt
import networkx.algorithms.community as nxcom
G = nx.karate_club_graph()
greedy = nxcom.greedy_modularity_communities(G)
#returns a list with type frozen sets within the list
#[{set1},{set2},{set3}]
pos = nx.spring_layout(G) # compute graph layout
plt.axis('off')
nx.draw_networkx_nodes(G, pos, cmap=plt.cm.RdYlBu, node_color=list(greedy.values()))
plt.show(G)
It looks like your issue comes from the way you are mapping colors to your communities. Since the node_color argument from nx.draw_networkx_nodes is expected to be a list of color (see doc here), you will need to associate each one of your nodes with the color of its community. You can do that by using:
c=plt.cm.RdYlBu(np.linspace(0,1,len(greedy))) #create a list of colors, one for each community
colors={list(g)[j]:c[i] for i,g in enumerate(greedy) for j in range(len(list(g)))} #associate each node with the color of its community
colors_sort=dict(sorted(colors.items())) #sort the dictionary by keys such
You can then convert the values of your sorted dictionnary into a list and pass it to the nx.draw_networkx_nodes with nx.draw_networkx_nodes(G, pos,node_color=list(colors_sort.values())).
See full code below:
import networkx as nx
import matplotlib.pyplot as plt
import networkx.algorithms.community as nxcom
import numpy as np
G = nx.karate_club_graph()
greedy = nxcom.greedy_modularity_communities(G)
c=plt.cm.RdYlBu(np.linspace(0,1,len(greedy)))
colors={list(g)[j]:c[i] for i,g in enumerate(greedy) for j in range(len(list(g)))}
colors_sort=dict(sorted(colors.items()))
pos = nx.spring_layout(G)
nx.draw_networkx_nodes(G, pos,node_color=list(colors_sort.values()))
nx.draw_networkx_edges(G, pos)
nx.draw_networkx_labels(G, pos,labels={n:str(n) for n in G.nodes()})
plt.axis('off')
plt.show(G)

Add text flush left below plot in python

I'd like to add text beneath a plot, which includes the source of the used data.
It should be positioned at the edge of the image, so beneath the longest ytick and if possible at a fixed vertical distance to the x-axis.
My approach:
import matplotlib.pyplot as plt
country = ['Portugal','Spain','Austria','Italy','France','Federal Republic of Germany']
value = [6,8,10,12,14,25]
plt.figure(figsize=(4,4))
plt.barh(country,value)
plt.xlabel('x-axis')
plt.text(-18,-2.5,'Source: blablablablablablablablablablablablablablablablabla',ha='left')
Plot of the code
I used plt.text(). My problem with the command is, that I have to manually try x and y values (in the code: -18,-2.5) for different plots.
Is there a better way?
Thanks in advance.
Firstly, I got the box info of yticklabels, and then got the leftmost x location for all the yticklabels. Finally, the blended transform method was used to add text with some location adjustments.
import matplotlib.pyplot as plt
from matplotlib.transforms import IdentityTransform
import matplotlib.transforms as transforms
country = ['Portugal','Spain','Austria','Italy','France','Federal Republic of Germany']
value = [6,8,10,12,14,25]
plt.figure(figsize=(4,4))
plt.barh(country,value)
plt.xlabel('x-axis')
ax = plt.gca()
fig =plt.gcf()
fig.tight_layout()
fig.canvas.draw()
labs = ax.get_yticklabels()
xlocs = []
for ilab in labs:
xlocs.append(ilab.get_window_extent().x0)
print(xlocs)
x0 = min(xlocs)
trans = transforms.blended_transform_factory(IdentityTransform(), ax.transAxes)
plt.text(x0-2.5,-0.2,'Source: blablablablablablablablablablablablablablablablabla',ha='left',transform=trans)
plt.savefig("flush.png",bbox_inches="tight")

Is there any way to show gray color to states which are not having any data in Plotly map?

I need to show gray color to the states which do not have any data in Plotly.
Sample csv file is: (This states have data)
States which are not having data are: (I have filled the missing values as -1
The current plots generated are: ( I need to show gray color to the states with missing data.
Thanks!
Your solution is to use custom colorscale in combination with
import plotly.express as px
px.choropleth_mapbox
The following is an example on how to use custom colorscale:
import plotly.plotly as py
import plotly.graph_objs as go
import numpy as np
import copy
import pandas as pd
# Read data from a csv
z_data = pd.read_csv('https://raw.githubusercontent.com/plotly/datasets/master/api_docs/mt_bruno_elevation.csv')
z=z_data.values.copy()
# Compute surface color with nan's
surfacecolor = z.copy()
surfacecolor[-10:, -10:] = np.nan
# Replace nans with -100
surfacecolor[np.isnan(surfacecolor)] = -100
# Build surface trace
data = [
go.Surface(
z=z,
surfacecolor=surfacecolor,
cmin = -5,
cmax = 350,
colorscale=[[0, 'gray'],
[0.01, 'gray'],
[0.01, 'blue'],
[1, 'red']]
)
]
# Build layout
layout = go.Layout(
title='Mt Bruno Elevation',
autosize=False,
width=500,
height=500,
margin=dict(
l=65,
r=50,
b=65,
t=90
)
)
fig = go.FigureWidget(data=data, layout=layout)
fig
A similar question has been solved by the plotly community forum.
Please find the plotly documentation on how to define custom colorscales.
Hope this solves your issue!

Plotly chart percentage with smileys

I would like o add a plot figure based on smileys like this one:
dat will come from a dataframe pandas : dataframe.value_counts(normalize=True)
Can some one give me some clues.
use colorscale in normal way for a heatmap
use anotation_text to assign an emoji to a value
import plotly.figure_factory as ff
import plotly.graph_objects as go
import pandas as pd
import numpy as np
df = pd.DataFrame([[j*10+i for i in range(10)] for j in range(10)])
e=["😃","🙂","😐","☚ī¸"]
fig = go.Figure(ff.create_annotated_heatmap(
z=df.values, colorscale="rdylgn", reversescale=False,
annotation_text=np.select([df.values>75, df.values>50, df.values>25, df.values>=0], e),
))
fig.update_annotations(font_size=25)
# allows emoji to use background color
fig.update_annotations(opacity=0.7)
update coloured emoji
fundamentally you need emojicons that can accept colour styling
for this I switched to Font Awesome. This then also requires switching to dash, plotly's cousin so that external CSS can be used (to use FA)
then build a dash HTML table applying styling logic for picking emoticon and colour
from jupyter_dash import JupyterDash
import dash_html_components as html
import pandas as pd
import branca.colormap
# Load Data
df = pd.DataFrame([[j*10+i for i in range(10)] for j in range(10)])
external_stylesheets = [{
'href': 'https://use.fontawesome.com/releases/v5.8.1/css/all.css',
'rel': 'stylesheet', 'crossorigin': 'anonymous',
'integrity': 'sha384-50oBUHEmvpQ+1lW4y57PTFmhCaXp0ML5d60M1M7uH2+nqUivzIebhndOJK28anvf',
}]
# possibly could use a a different library for this - simple way to map a value to a colormap
cm = branca.colormap.LinearColormap(["red","yellow","green"], vmin=0, vmax=100, caption=None)
def mysmiley(v):
sm = ["far fa-grin", "far fa-smile", "far fa-meh", "far fa-frown"]
return html.Span(className=sm[3-(v//25)], style={"color":cm(v),"font-size": "2em"})
# Build App
app = JupyterDash(__name__, external_stylesheets=external_stylesheets)
app.layout = html.Div([
html.Table([html.Tr([html.Td(mysmiley(c)) for c in r]) for r in df.values])
])
# Run app and display result inline in the notebook
app.run_server(mode='inline')

set_xlim() does not work with text labels

I am trying to zoom in on geopandas map with labels using set_xlim() in with matplotlib. I basically adapted this SO question to add labels to a map.
However, set_xlim() does not seem to work and did not zoom in on the given extent. (By the way, I've also tried to use text() instead of annotate(), to no avail.)
What I did was the following:
I used the same US county data as in the question linked above, extracted the files, and then executed the following in Jupyter notebook:
import geopandas as gpd
import matplotlib.pyplot as plt
%matplotlib inline
shpfile='shp/cb_2015_us_county_20m.shp'
gdf=gpd.read_file(shpfile)
gdf.plot()
, which gives a map of all US counties as expected:
Adding labels as with one of the answers also works:
ax = gdf.plot()
gdf.apply(lambda x: ax.annotate(s=x.NAME, xy=x.geometry.centroid.coords[0], ha='center'),axis=1);
However, when trying to zoom in to a particular geographic extent with set_xlim() and set_ylim() as follows:
ax = gdf.plot()
gdf.apply(lambda x: ax.annotate(s=x.NAME, xy=x.geometry.centroid.coords[0], ha='center'),axis=1);
ax.set_xlim(-84.2, -83.4)
ax.set_ylim(42, 42.55)
, the two functions do not seem to work. Instead of zooming in, they just trimmed everything outside of the given extent.
If the labeling code is dropped out (gdf.apply(lambda x: ax.annotate(s=x.NAME, xy=x.geometry.centroid.coords[0], ha='center'),axis=1);, the set_xlim() works as expected:
My question is:
What is the correct way to zoom in to an area when labels are present in a plot?
You need some coordinate transformation.
import cartopy.crs as ccrs
# relevant code follows
# set numbers in degrees of longitude
ax.set_xlim(-84.2, -83.4, ccrs.PlateCarree())
# set numbers in degrees of latitude
ax.set_ylim(42, 42.55, ccrs.PlateCarree())
plt.show()
with the option ccrs.PlateCarree(), the input values are transformed to proper data coordinates.
When I try it, I can't draw on matplotlib with the axes restricted. So it's possible to extract the data.
import geopandas as gpd
import matplotlib.pyplot as plt
%matplotlib inline
fig,ax = plt.subplots(1,1, figsize=(4,4), dpi=144)
shpfile = './cb_2015_us_county_20m/cb_2015_us_county_20m.shp'
gdf = gpd.read_file(shpfile)
# gdf = gdf.loc[gdf['STATEFP'] == '27']
gdf['coords'] = gdf['geometry'].apply(lambda x: x.representative_point().coords[:])
gdf['coords'] = [coords[0] for coords in gdf['coords']]
gdf = (gdf[(gdf['coords'].str[0] >= -84.2) & (gdf['coords'].str[0] <= -83.4)
& (gdf['coords'].str[1] >= 42) & (gdf['coords'].str[1] <= 42.55)])
gdf.plot(ax=ax)
gdf.apply(lambda x: ax.annotate(text=x.NAME, xy=x.geometry.centroid.coords[0], ha='center'),axis=1)