Creating Leaflet Map with Python and Folium - python-3.x

Here is the code:
for lat,lon,name,elev in zip(df['LAT'],df['LON'],df['NAME'],df['ELEV']):
fg.add_child(folium.Marker(location=
[lat,lon],popup=name,icon=folium.Icon(color=color_ori(elev))))
I am creating a map for volcanoes in the USA, and I want to show a marker with their names in the popup. I can't do that with the code above, but when I use popup=str(elev)+"m", it works fine. How do I include the names from my CSV file into a popup ?

You can just apply a method add_child to each marker with a popup object as an argument.
The code is:
import folium
lats = range(59, 63)
lons = range(10, 14)
names = ['marker' + str(i) for i in range(4)]
elevations = range(4)
m = folium.Map([60, 10], tiles='Mapbox Bright', zoom_start=5)
for lat, lon, name, elev in zip(lats, lons, names, elevations):
folium.Marker([lat, lon], icon=folium.Icon(color='red')).add_child(folium.Popup(name)).add_to(m)
Output:

Related

Plotly : How to enable text label in line graph for the last value?

I am trying to build a graph where the line graph should show the value of only the last element in some beautiful formating.
line graph with no text at end
Now the current method of the text shows for all elements and is a straight text that creates a lot of collisions with different lines in the same graph and looks clumsy.
Will be very nice to achieve something as mentioned in the below image.
desired line graph with text
This is now handled through:
legendgroup = d.name
Plot 1: All
Plot 2: Deselect GOOG in the legend and see that the marker disappears as well:
Complet code:
# imports
import pandas as pd
import plotly.express as px
# data
df = px.data.stocks()
df = df.drop('AMZN', axis = 1)
colors = px.colors.qualitative.T10
# plotly
fig = px.line(df,
x = 'date',
y = [c for c in df.columns if c != 'date'],
template = 'plotly_dark',
color_discrete_sequence = colors,
title = 'Stocks',
)
# move legend
fig.layout.legend.x = -0.3
# add traces for annotations and text for end of lines
for i, d in enumerate(fig.data):
fig.add_scatter(x=[d.x[-1]], y = [d.y[-1]],
mode = 'markers+text',
text = d.y[-1],
textfont = dict(color=d.line.color),
textposition='middle right',
marker = dict(color = d.line.color, size = 12),
legendgroup = d.name,
showlegend=False)
fig.show()

Use customized markers in FastMarkerCluster in Python folium?

I'm able to add data to a folium map using MarkerCluster and vary the parameters of the marker according to parameters for each point.
###Toy Example 1###
import pandas as pd
import numpy as np
import folium
import folium.plugins as plugins
lats = np.asarray([-54.4, -54.5, -54.6])
longs = np.asarray([-69.5, -69.5, -69.5])
data = np.asarray([70, 90, 5])
colour = np.asarray(['green', 'orange', 'red'])
dummy = [list(a) for a in zip(lats, longs, data, colour)]
dummy2 = pd.DataFrame(dummy, columns=['lat', 'lng', 'data', 'colour'])
dmap = folium.Map(location = [-54.5, -69.5], zoom_start = 7)
mc=plugins.MarkerCluster()
for a, row in dummy2.iterrows():
folium.CircleMarker(location=[row[0],row[1]],
radius=row[2], color=row[3], fill=True).add_to(mc)
mc.add_to(dmap)
dmap
As my use case has thousands of points, I would like to do something similar to the above with FastMarkerCluster. The farthest I've gone to achieving this is is:
###Toy Example 2###
callback = """\
function (row) {
var marker;
marker = L.circle(new L.LatLng(row[0], row[1]), {color:'red'});
return marker;
};
"""
lats = np.asarray([-54.4, -54.5, -54.6])
longs = np.asarray([-69.5, -69.5, -69.5])
data = np.asarray([70, 90, 5])
colour = np.asarray(['green', 'orange', 'red'])
dummy = [list(a) for a in zip(lats, longs)]
dummy2 = pd.DataFrame(dummy, columns=['lat', 'lng'])
dmap = folium.Map(location = [-54.5, -69.5], zoom_start = 7)
plugins.FastMarkerCluster(dummy, callback=callback).add_to(dmap)
dmap
I can envision adding {radius: row[2], color: row[3]} to the callback to get the functionality I want. However, if I add the data and/or colour arrays to dummy (as in Toy Example 1), I get TypeError: must be real number, not numpy.str_. If I use dummy2 in Toy Example 2, I get TypeError: must be real number, not str.
Any help on this is greatly appreciated.
f
Cheers,
-R
This is now solved in the Folium master branch on github thanks to Conegmo over there. There had been an issue where the column names were being read as part of the data. At the moment, installing Folium using the following means that all of the dummy examples I posted will work.
pip install git+https://github.com/python-visualization/folium.git

[Bokeh]Getting `TypeError: Object of type Polygon is not JSON serializable`

First time posting in stackoverflow. Hope you guys are doing well.
Recently, I am trying to create a filterable Bokeh graph to graph a US map base on the filter an user select. However, when I try to fit a geometry to ColumnDataSource. It is giving me an error: TypeError: Object of type Polygon is not JSON serializable when I run show(figure)
The code below shows how I want to update geometry to ColumnDataSource and I get an error
# <------- This is where the graph starts------->
# reset the graph
reset_output()
# import data
data = gpd.read_file("/Users/xxxx/Desktop/cb_2015_us_state_500k/cb_2015_us_state_500k.shp", encoding="utf-8")
data1 = data[~data.STUSPS.isin(['AK','AS', 'GU', 'HI', 'PR','MP', 'VI'])]
data2 = data[data.STUSPS.isin(['TX', 'UT'])]
# get a list of unique value
unique_state = sorted(list(data2.NAME.unique()))
select = Select(title="State", options=unique_state)
# get data into ColumnDataSource
source=ColumnDataSource(ColumnDataSource.from_df(data2.loc[:]))
# crate filtered dataframe
filteredSource = ColumnDataSource(data=dict(STUSPS=[],NAME=[],ALAND=[]))
columns = [TableColumn(field="NAME",title="NAME",sortable=True),
TableColumn(field="STUSPS",title="STUSPS",sortable=True),
TableColumn(field="ALAND",title="ALAND",sortable=True),
TableColumn(field="geometry",title="geometry",sortable=True)]
data_table=DataTable(source=filteredSource,columns=columns, width=800 )
# <---- Call back starts ---->
callback = CustomJS(args=dict(source=source,
filteredSource=filteredSource,
data_table=data_table), code="""
var data = source.data;
var f = cb_obj.value;
var d2 = filteredSource.data;
d2['STUSPS']=[]
d2['NAME']=[]
d2['ALAND']=[]
d2['geometry']=[]
for(i = 0; i < data['NAME'].length;i++){
if(data['NAME'][i]==f){
d2['STUSPS'].push(data['STUSPS'][i])
d2['NAME'].push(data['NAME'][i])
d2['ALAND'].push(data['ALAND'][i])
d2['geometry'].push(data['geometry'][i])
}
}
filteredSource.change.emit()
// trigger change on datatable
data_table.change.emit()
""")
select.js_on_change('value',callback)
layout = column(widgetbox(select, data_table))
# output_file("filter.html", title="filter example")
show(layout)
Afterwards, I saw an example directly fitting shape file to dictionary or dataframe which may solve the problem. Here is the link:
Bokeh Mapping Counties
However, when I using the code to graph it. it is giving me
ValueError: Out of range float values are not JSON compliant
This is the code I run:
import shapefile
import itertools
shp = open("/Users/xxxx/Desktop/cb_2015_us_state_500k/cb_2015_us_state_500k.shp", "rb")
dbf = open("/Users/xxxx/Desktop/cb_2015_us_state_500k/cb_2015_us_state_500k.dbf", "rb")
sf = shapefile.Reader(shp=shp, dbf=dbf)
lats = []
lons = []
ct_name = []
st_id = []
ct_state_name = []
for shprec in sf.shapeRecords():
st_id.append(int(shprec.record[0]))
ct_name.append(shprec.record[5])
ct_state_name.append(shprec.record[4])
lat, lon = map(list, zip(*shprec.shape.points))
indices = shprec.shape.parts.tolist()
lat = [lat[i:j] + [float('NaN')] for i, j in zip(indices, indices[1:]+[None])]
lon = [lon[i:j] + [float('NaN')] for i, j in zip(indices, indices[1:]+[None])]
lat = list(itertools.chain.from_iterable(lat))
lon = list(itertools.chain.from_iterable(lon))
lats.append(lat)
lons.append(lon)
map_data = pd.DataFrame({'x': lats, 'y': lons, 'state': st_id, 'county_name': ct_name, 'ct_state_name': ct_state_name})
map_data_m = map_data[map_data.ct_state_name.isin(['NJ'])]
source = ColumnDataSource(map_data_m)
TOOLS="pan,wheel_zoom,box_zoom,reset,hover,save"
p = figure(title="Title", tools=TOOLS,
x_axis_location=None, y_axis_location=None)
p.grid.grid_line_color = None
p.patches('x', 'y', source=source,
fill_color='color', fill_alpha=0.7,
line_color="white", line_width=0.5)
show(p)
anyone who is able to help me to resolve either of the question? I have been stuck for few days. Thanks a lot!
Could you check if there are no infinite numbers in the dataset?
Reference: https://stackoverflow.com/a/47085304/2908623

Matplotlib figure annotations outside of window

I am making a program that implements a matplotlib pie/donut chart into a tkinter window to illustrate some data, however, I have added "annotations" or labels from each wedge of the pie chart. Because of this the window that opens when I execute the code fits the chart itself, but the labels are cut off at the edges of the window. Specifically, it looks like this...
Note the top two arrows don't actually have text attached to the corresponding labels so the situation is actually worse than my screenshot depicts.
Even if I get rid of the code related to generating a tkinter GUI, and just try to execute code to generate a regular figure window the labels are initially cut-off. But, if I use the built in zoom-out functionality I can zoom out the make the labels fit.
I have tried to adjust the figsize here...
fig, ax = plt.subplots(figsize=(6, 4), subplot_kw=dict(aspect="equal"))
yet it makes no difference. Hopefully there is a solution, thanks...
Here is my full code if anyone needs...
import numpy as np
import matplotlib.pyplot as plt
player1_cards = {'Mustard', 'Plum', 'Revolver', 'Rope', 'Ballroom', 'Library'}
player2_cards = {'Scarlet', 'White', 'Candlestick'}
player3_cards = {'Green', 'Library', 'Kitchen', 'Conservatory'}
middle_cards = {'Peacock'}
unknown_cards = {'Lead Pipe', 'Wrench', 'Knife', 'Hall', 'Lounge', 'Dining Room', 'Study'}
player1_string = ', '.join(player1_cards)
player1_string = player1_string.replace(', ', '\n')
player2_string = ', '.join(player2_cards)
player2_string = player2_string.replace(', ', '\n')
player3_string = ', '.join(player3_cards)
player3_string = player3_string.replace(', ', '\n')
fig, ax = plt.subplots(figsize=(6, 4), subplot_kw=dict(aspect="equal"))
recipe = [player1_string, player2_string, player3_string, '', '']
data = [len(player1_cards), len(player2_cards), len(player3_cards), 1, 7]
cols = ['#339E5A', '#26823E', '#0C5D2E', '#98D6AE', '#5EC488']
wedges, texts = ax.pie(data, wedgeprops=dict(width=0.5), startangle=90, colors = cols)
for w in wedges:
w.set_linewidth(4)
w.set_edgecolor('white')
bbox_props = dict(boxstyle="square,pad=0.3", fc="w", ec="white", lw=0.72)
kw = dict(xycoords='data', textcoords='data', arrowprops=dict(arrowstyle="-"), bbox=bbox_props, zorder=0, va="center")
for i, p in enumerate(wedges):
ang = (p.theta2 - p.theta1)/2. + p.theta1
y = np.sin(np.deg2rad(ang))
x = np.cos(np.deg2rad(ang))
horizontalalignment = {-1: "right", 1: "left"}[int(np.sign(x))]
connectionstyle = "angle,angleA=0,angleB={}".format(ang)
kw["arrowprops"].update({"connectionstyle": connectionstyle})
ax.annotate(recipe[i], xy=(x, y), xytext=(x + np.sign(x)*.5, y*1.5),
horizontalalignment=horizontalalignment, **kw, family = "Quicksand")
ax.set_title("Matplotlib bakery: A donut")
plt.show()
You would want to play around with the subplot parameters to make space for the text outside the axes.
fig.subplots_adjust(bottom=..., top=..., left=..., right=...)
E.g. in this case
fig.subplots_adjust(bottom=0.2, top=0.9)
seems to give a nice representation

how to hide index 0 of PANDAS and Jupyter

i want to hide de index 0 (rows) from my jupyter notebook:
display(df.style.set_properties(**{'text-align': 'left'})
the method i use is style in order to get it as an html and left align,
where df is a simple PANDAS dataframe.
THANK YOU!
It is very unclear what you want. So I'm going to take a guess.
Consider the dataframe df
df = pd.DataFrame([[1, 2], [3, 4], [5, 6]], list('ABC'), list('XY'))
Since simply "hiding" the first row is super easy as #chtonicdaemon pointed out:
df.iloc[1:]
X Y
B 3 4
C 5 6
I'm going to assume you wanted something different. What I've seen asked before is how do I not display the index? I'll first point at a previous answer of mine ColorChange
This is the function I'll use to apply my own css
def HTML_with_style(df, style=None, random_id=None):
from IPython.display import HTML
import numpy as np
import re
df_html = df.to_html()
if random_id is None:
random_id = 'id%d' % np.random.choice(np.arange(1000000))
if style is None:
style = """
<style>
table#{random_id} {{color: blue}}
</style>
""".format(random_id=random_id)
else:
new_style = []
s = re.sub(r'</?style>', '', style).strip()
for line in s.split('\n'):
line = line.strip()
if not re.match(r'^table', line):
line = re.sub(r'^', 'table ', line)
new_style.append(line)
new_style = ['<style>'] + new_style + ['</style>']
style = re.sub(r'table(#\S+)?', 'table#%s' % random_id, '\n'.join(new_style))
df_html = re.sub(r'<table', r'<table id=%s ' % random_id, df_html)
return HTML(style + df_html)
And I'll call it with my own css to suppress the index
style = """
<style>
table tr :first-child{display: none;}
</style>
"""
HTML_with_style(df, style)
This doesn't work for print(df)

Resources