Adding a Title or Text to a Folium Map - python

I'm wondering if there's a way to add a title or text on a folium map in python?
I have 8 maps to show and I want the user to know which map they're looking at without having to click on a marker. I attempted to add an image of the map, but couldn't because I don't have high enough reputation score.
My code:
#marker cluster
corpus_chris_loc = [27.783889, -97.510556]
harvey_avg_losses_map = folium.Map(location = corpus_chris_loc, zoom_start = 5)
marker_cluster = MarkerCluster().add_to(harvey_avg_losses_map)
#inside the loop add each marker to the cluster
for row_index, row_values in harvey_losses.iterrows():
loc = [row_values['lat'], row_values['lng']]
pop = ("zip code: " + str(row_values["loss_location_zip"]) + "\nzip_avg: " + "$" + str(row_values['zip_avg'])) #show the zip and it's avg
icon = folium.Icon(color='red')
marker = folium.Marker(
title = "Harvey: " + "$" + str(row_values['harvey_avg']),
location = loc,
popup = pop,
icon=icon)
marker.add_to(marker_cluster)
#save an interactive HTML map by calling .save()
harvey_avg_losses_map.save('../data/harveylossclustermap.html')
harvey_avg_losses_map[map of hurricane harvey insurance claims][1]

Of course you can add a title to a Folium map.
For example:
import folium
loc = 'Corpus Christi'
title_html = '''
<h3 align="center" style="font-size:16px"><b>{}</b></h3>
'''.format(loc)
m = folium.Map(location=[27.783889, -97.510556],
zoom_start=12)
m.get_root().html.add_child(folium.Element(title_html))
m.save('map-with-title.html')
m

Related

Folium put markers in marker clusters AND in layers based on a value

So, I'm working with a dataset of stores, each store with its lat, lng, name and category.
Since we are talking about several hundreds of even thousands of stores, I'm using marker clusters, and they are working fine...
Now, I need to also set these stores in different layers based on their category, so that when I click on say "electronics stores", I only get those stores in the map (and they should be removed from the marker cluster as well)
Consider this sample data:
stores = [(-23.5578906,-46.6665546, 'store1','electronics'),
(-23.562711,-46.674363, 'store2','home goods'),
(-23.5642399,-46.6681833, 'store3','beauty'),
(-23.584167,-46.678497, 'store4','electronics'),
(-23.5956238,-46.6865377, 'store5','electronics'),
(-23.5868682,-46.6773554,'store6','home goods'),
(-23.6011096,-46.6739275, 'store7','beauty'),
(-23.6087354,-46.6973713, 'store8','home goods'),
(-23.5943515,-46.6846959, 'store9','beauty')]
My code works ok for putting the markers in clusters, but when I try to also add them to layers based on their categories it doesn't work. I get no errors, and the map "loads", but the markers and clusters don't get displayed, and I get no layers on the map.
This is my code:
mymap = folium.Map(location=[y_map, x_map], zoom_start=11,tiles=None)
folium.TileLayer(name="Mapbox Bright",control=False).add_to(mymap)
markers_list = []
all_gp = []
for lat, lng, name, category zip(df_stores['LAT'],
df_stores['LNG'],
df_stores['NAME'],
df_stores['CATEGORY']
):
html = '''NAME: ''' + name + '''<br>CATEGORY: ''' + category
iframe = folium.IFrame(html,
width=300,
height=130)
popup = folium.Popup(iframe,
max_width=300)
lead_marker = folium.Marker(
[lat, lng],
popup=popup,
icon=folium.Icon(color='purple', icon='glyphicon-cutlery', prefix='glyphicon')
)
markers_list.append(lead_marker)
pg = category
all_gp.append(pg)
mCluster = MarkerCluster(name="Stores").add_to(mymap)
for pnt in markers_list:
pnt.add_to(mCluster)
######################################################################
# Create point_layer object
unique_gp = list(set(all_gp))
vlist = []
for i,k in enumerate(unique_gp):
locals()[f'point_layer{i}'] = folium.FeatureGroup(name=k)
vlist.append(locals()[f'point_layer{i}'])
# Creating list for point_layer
pl_group = []
for n in all_gp:
for v in vlist:
if n == vars(v)['layer_name']:
pl_group.append(v)
for pnt,pg in zip(markers_list,pl_group):
pnt.add_to(pg)
pg.add_to(mymap)
######################################################################
folium.LayerControl().add_to(mymap)
mymap.add_child(MeasureControl())
mymap.render()
mymap.save('stores.html')
The code between the lines of ############ I took form another post here (How to add categorical layered data to LayerControl() in python Folium map?) and adapted it to my code, but it seems I'm missing something. If I take out the last for cycle from the code, the map loads correctly with its clusters working ok, any suggestions?
I will answer with the understanding that the question is how to create a category layer, add markers for the information that belongs to it, and control the show/hide with a layer control. First, set the respective column data from the row information in the data frame and add the pop-up information. Add the category information based on the category information to the pre-prepared per-category layer.
import pandas as pd
import numpy as np
import folium
from folium.plugins import MarkerCluster
stores = [(-23.5578906,-46.6665546, 'store1','electronics'),
(-23.562711,-46.674363, 'store2','home goods'),
(-23.5642399,-46.6681833, 'store3','beauty'),
(-23.584167,-46.678497, 'store4','electronics'),
(-23.5956238,-46.6865377, 'store5','electronics'),
(-23.5868682,-46.6773554,'store6','home goods'),
(-23.6011096,-46.6739275, 'store7','beauty'),
(-23.6087354,-46.6973713, 'store8','home goods'),
(-23.5943515,-46.6846959, 'store9','beauty')]
df = pd.DataFrame(stores, columns=['LAT','LNG','NAME','CATEGORY'])
mymap = folium.Map(location=[df['LAT'].mean(), df['LNG'].mean()], zoom_start=12)
#mCluster = MarkerCluster(name="Stores").add_to(mymap)
mCluster_hg = MarkerCluster(name="home goods").add_to(mymap)
mCluster_ele = MarkerCluster(name="electronics").add_to(mymap)
mCluster_bea = MarkerCluster(name="beauty").add_to(mymap)
for row in df.itertuples():
#print(row)
location = row[1], row[2]
icon=folium.Icon(color='purple', icon='glyphicon-cutlery', prefix='glyphicon')
html = '''NAME: ''' + row[3] + '''<br>CATEGORY: ''' + row[4]
iframe = folium.IFrame(html, width=300, height=130)
popup = folium.Popup(iframe, max_width=300)
marker = folium.Marker(location=location, popup=popup, icon=icon)
#folium.Popup(popup).add_to(marker)
#mCluster_bea.add_child(marker)
if row[4] == 'electronics':
mCluster_ele.add_child(marker)
elif row[4] == 'home goods':
mCluster_hg.add_child(marker)
elif row[4] == 'beauty':
mCluster_bea.add_child(marker)
folium.LayerControl().add_to(mymap);
mymap

How can combine geometry with countries?

I need a file in which I will have the names of European and Asian countries and their "geometry" data.
Part of Russia was jumping to the other side of the chart and I had to correct the data to keep the Russia map in one piece.
I found a code for it, but unfortunately when I execute it, it only keeps geometry data, which cannot be easily linked with the names of countries that I need
Initial map:
enter image description here
Code:
world = geopandas.read_file(geopandas.datasets.get_path('naturalearth_lowres'))
asia = world[world["continent"] == "Asia"]
europe = world[world["continent"] == "Europe"]
euroasia = pd.concat([asia, europe])
name = euroasia["name"]
def shift_geom(shift, gdataframe, plotQ=False):
shift -= 180
moved_geom = []
splitted_geom = []
border = LineString([(shift,90),(shift,-90)])
for row in gdataframe["geometry"]:
splitted_geom.append(split(row, border))
for element in splitted_geom:
items = list(element)
for item in items:
minx, miny, maxx, maxy = item.bounds
if minx >= shift:
moved_geom.append(translate(item, xoff=-180-shift))
else:
moved_geom.append(translate(item, xoff=180-shift))
moved_geom_gdf = gpd.GeoDataFrame({"geometry": moved_geom})
if plotQ:
fig1, ax1 = plt.subplots(figsize=[20,30])
moved_geom_gdf.plot(ax=ax1)
plt.show()
return moved_geom_gdf
new = shift_geom(90, euroasia, False)
n_ = shift_geom(-90, new, True)
new
Results (good map, but only geometry data):
enter image description here

Updating folium changed the Popup box width

Recently I updated folium from 0.5.0 to 0.11.0 and thereafter I am experiencing a problem with the popup box. With the update the popup box seem to have shrinked in width and the text is coming in separate lines, which happened to appear in the same line with the previous version of folium. No changes been made with the code.
How can I change the popup box look like the previous one, i.e., text does not break the line?
Popup box code:
fgc.add_child(folium.Marker(location=[lt, ln], popup= "<h4> <b>Thana :&nbsp" + di +"</h4></b>"+ "<br><b>Cases Total: &nbsp: </b>"+str(ca)+ " person "+ "<br>" + "<b>Cases 24 hours : </b>"+ str(da)+ " person "+"<br>"+"<b>Cases 7 days: </b>"+str(we)+ " person "+"<br><b>Neighbouhood affected : </b>"+str(ne)
How I handled this was to create a IFrame to handle the dataframe variables and then just passed the that to the popup class, this should work for database or dataframe.
for (index, row) in df.iterrows():
if row.loc['BRANCH'] == 1:
iframe = folium.IFrame('Account#:' + str(row.loc['ACCT']) + '<br>' + 'Name: ' + row.loc['NAME'] + '<br>' + 'Terr#: ' + str(row.loc['TERR']))
popup = folium.Popup(iframe, min_width=300, max_width=300)
folium.Marker(location=[row.loc['LAT'], row.loc['LON']], icon=folium.Icon(color=row.loc['COLOR'], icon='map-marker', prefix='fa'), popup=popup).add_to(map1)
Without reproducible code it is not possible to give you a tailored solution. As a general suggestion, you could use folium.Popup() with the combo of min_width and max_width parameters to force the width of a popup.
For example:
import folium
m = folium.Map(location=[43.775, 11.254],
zoom_start=5)
html = '''1 aaaaaaaaaaaaaaaaaa aaaa aaa aa aaaaa aaa aaaa a a a a<br>2 aaaaaaaaaa aaa aaaaa aaaaa<br>3 aaaaa aaaaaa aaaaa aaa aaaaa<br>4 aaa aaa aaaaaaaa
'''
iframe = folium.IFrame(html)
popup = folium.Popup(iframe,
min_width=500,
max_width=500)
marker = folium.Marker([43.775, 11.254],
popup=popup).add_to(m)
m
and you get:
def color(elev):
if elev == "STARTED":
col = 'orange.png'
elif elev=="COMPLETED":
col = 'vehicle3_w30.png'
elif elev =="DELIVERED":
col = 'vehicle3_w30.png'
else:
col='grey.png'
return col
icon_url = "grey.png"
icon = folium.features.CustomIcon(icon_url,
icon_size=(12, 12))
for lat,lan,name,event_name,officer,update_at in zip(df['fSourceLatitude'],df['fSourceLongitude'],df['officer_name'],df['event_name'],df["user_name"],df["update_at"]):
bikeColor = color(event_name)
biker = folium.features.CustomIcon(bikeColor, icon_size=(20,40))
popContent = ("Updated At: " + str(update_at) + '<br>' +\
"Officer ID : " + str(officer) + '<br>'+\
"Status: {}".format(event_name))
iframe = folium.IFrame(popContent)
popup1 = folium.Popup(iframe,
min_width=500,
max_width=500)
folium.Marker(location=[lat,lan],popup = popup1,icon= biker).add_to(map5)
It worked for me, you need to initiate marker with custom icon in each iteration as shown in this code, It will work perfectly...
He is trying to fetch the data from database that's why it is breaking, If he did write the data using html tag then there will be no problem. But the main fact inside of html tag you have to use fetch data.

Altair - how to use datum to format a tooltip

I am trying to format a tooltip using datum but, so far, without any success.
The tooltip I need is the something like "INV: 174,000.00". How can I do it?
This is where I supposed to use datum:
text = line.mark_text(align='right', dx=-10, dy=-10).encode(
text=alt.condition(nearest, f'Revenue:Q', alt.value(' '))
).transform_calculate(label='"INV: " + datum.Revenue')
Full code:
import altair as alt
from altair import datum
import pandas as pd
import numpy as np
import os
def areaChart():
df = {
'Stat': ['INV'],
'Revenue': [474147.84, 2170326.05, 2184077.88, 3957965.97]
}
source = pd.DataFrame(np.cumsum(df, 0),
columns='Revenue', index=pd.RangeIndex(len(df['Stat']), name='Revenue'))
print(source)
source = source.reset_index().melt('Revenue', var_name='Analyzing', value_name='Revenue')
nearest = alt.selection(type='single', nearest=True, on='mouseover',
fields=['Revenue'], empty='none')
line = alt.Chart(source).mark_area(opacity=0.60).encode(
x=alt.X(f'Stat:Q', axis=alt.AxisConfig()),
y=f'Revenue:Q',
color='Analyzing:N'
)
selectors = alt.Chart(source).mark_point().encode(
x=f'Stat:Q',
opacity=alt.value(0),
).add_selection(
nearest
)
points = line.mark_point().encode(
opacity=alt.condition(nearest, alt.value(1), alt.value(0))
).properties(
title=f'Stat x INV'
)
text = line.mark_text(align='right', dx=-10, dy=-10).encode(
text=alt.condition(nearest, f'Revenue:Q', alt.value(' '))
).transform_calculate(label='"INV: " + datum.Revenue')
rules = alt.Chart(source).mark_rule(color='black', size=0.70).encode(
x=f'Stat:Q',
).transform_filter(
nearest
)
chart = alt.layer(
line, selectors, points, rules, text
)
return chart
It looks like you are calculating the label, but never actually using it in an encoding. Try this instead:
text = line.mark_text(align='right', dx=-10, dy=-10).encode(
text=alt.condition(nearest, 'label:N', alt.value(' '))
).transform_calculate(label='"INV: " + datum.Revenue')

Vertical positioning of nodes in Sankey diagram to avoid collision with links

I'm trying to make a Sankey-plot using Plotly, which follows the filtering of certain documents into either being in scope or out of scope, i.e. 1 source, 2 targets, however some documents are filtered during step 1, some during step 2 etc. This leads to the following Sankey-plot:
Current output
Now what I would ideally like is for it to look something like this:
Ideal output
I've already tried to look through the documentation on : https://plot.ly/python/reference/#sankey but I fail to find what I'm looking for, ideally I would like to implement a feature to prevent the plot from overlapping nodes and links.
This is the code I'm using the generate the plot object:
def genSankeyPlotObject(df, cat_cols=[], value_cols='', visible = False):
### COLORPLATTE TO USE
colorPalette = ['472d3c', '5e3643', '7a444a', 'a05b53', 'bf7958', 'eea160', 'f4cca1', 'b6d53c', '71aa34', '397b44',
'3c5956', '302c2e', '5a5353', '7d7071', 'a0938e', 'cfc6b8', 'dff6f5', '8aebf1', '28ccdf', '3978a8',
'394778', '39314b', '564064', '8e478c', 'cd6093', 'ffaeb6', 'f4b41b', 'f47e1b', 'e6482e', 'a93b3b',
'827094', '4f546b']
### CREATES LABELLIST FROM DEFINED COLUMNS
labelList = []
for catCol in cat_cols:
labelListTemp = list(set(df[catCol].values))
labelList = labelList + labelListTemp
labelList = list(dict.fromkeys(labelList))
### DEFINES THE NUMBER OF COLORS IN THE COLORPALLET
colorNum = len(df[cat_cols[0]].unique()) + len(df[cat_cols[1]].unique()) + len(df[cat_cols[2]].unique())
TempcolorPallet = colorPalette * math.ceil(len(colorPalette)/colorNum)
shuffle(TempcolorPallet)
colorList = TempcolorPallet[0:colorNum]
### TRANSFORMS DF INTO SOURCE -> TARGET PAIRS
for i in range(len(cat_cols)-1):
if i==0:
sourceTargetDf = df[[cat_cols[i],cat_cols[i+1],value_cols]]
sourceTargetDf.columns = ['source','target','count']
else:
tempDf = df[[cat_cols[i],cat_cols[i+1],value_cols]]
tempDf.columns = ['source','target','count']
sourceTargetDf = pd.concat([sourceTargetDf,tempDf])
sourceTargetDf = sourceTargetDf.groupby(['source','target']).agg({'count':'sum'}).reset_index()
### ADDING INDEX TO SOURCE -> TARGET PAIRS
sourceTargetDf['sourceID'] = sourceTargetDf['source'].apply(lambda x: labelList.index(x))
sourceTargetDf['targetID'] = sourceTargetDf['target'].apply(lambda x: labelList.index(x))
### CREATES THE SANKEY PLOT OBJECT
data = go.Sankey(node = dict(pad = 15,
thickness = 20,
line = dict(color = "black",
width = 0.5),
label = labelList,
color = colorList),
link = dict(source = sourceTargetDf['sourceID'],
target = sourceTargetDf['targetID'],
value = sourceTargetDf['count']),
valuesuffix = ' ' + value_cols,
visible = visible)
return data

Categories