Python Folium MarkerCluster Color Customization - python

I'm creating a leaflet map in folium using MarkerCluster. I have been all over the documentation and searched for examples, but I cannot figure out how to customize the color for a given MarkerCluster or FeatureGroup (e.g., one set in green rather than default blue).
I tried creating the markers individually and iteratively adding them to the MarkerCluster, and that gave me the color I wanted, but then the iFrame html table woudn't function properly, and the popups were not appearing.
The code I've written works flawlessly (an html table used for popups is not supplied), but I'd really like to be able to change the color for one set of markers and retain the popups using the methods in my code. Any guidance would be greatly appreciated!
or_map = folium.Map(location=OR_COORDINATES, zoom_start=8)
res_popups, res_locations = [], []
com_popups, com_locations = [], []
for idx, row in geo.iterrows():
if row['Type'] == 'Residential':
res_locations.append([row['geometry'].y, row['geometry'].x])
property_type = row['Type']
property_name = row['Name']
address = row['address']
total_units = row['Total Unit']
iframe = folium.IFrame(table(property_type, property_name,
address, total_units), width=width,
height=height)
res_popups.append(iframe)
else:
com_locations.append([row['geometry'].y, row['geometry'].x])
property_type = row['Type']
property_name = row['Name']
address = row['address']
total_units = row['Total Unit']
iframe = folium.IFrame(table(property_type, property_name, address,
total_units), width=width,
height=height)
com_popups.append(iframe)
r = folium.FeatureGroup(name='UCPM Residential Properties')
r.add_child(MarkerCluster(locations=res_locations, popups=res_popups))
or_map.add_child(r)
c = folium.FeatureGroup(name='UCPM Commercial Properties')
c.add_child(MarkerCluster(locations=com_locations, popups=com_popups))
or_map.add_child(c)
display(or_map)

Instead of just dumping all your locations into the Cluster, you could loop over them and create a Marker for each of them - that way you can set the Marker's color. After creation, you can add the Marker to the desired MarkerCluster.
for com_location, com_popup in zip(com_locations, com_popups):
folium.Marker(com_location,
popup=com_popup
icon=folium.Icon(color='red', icon='info-sign')
).add_to(cluster)
A different approach would be to modify the style function, as shown here (In[4] and In[5]).

Related

Folium - add larger pop ups with data from XML file

I would like to create a table-like pop-up for my folium map but don't know how to do it (I'm a novice).
My data comes from an XML file that contains the gps coordinates, name, sales, etc. of stores.
Right now I can display the name of the stores in the pop-up, but I would also like to display the sales and other information below the name.
I reckon I should maybe use GeoJson but I don't know how to implement it in the code I already have (which contains clusterization) :
xml_data = 'Data Stores.xml'
tree = ElementTree.parse(xml_data)
counter = tree.find('counter')
name = counter.find('Name')
counter.find('Latitude').text
name = []
latitude = []
longitude = []
for c in tree.findall('counter'):
name.append(c.find('Name').text)
latitude.append(c.find('Latitude').text)
longitude.append(c.find('Longitude').text)
df_counters = pd.DataFrame(
{'Name' : name,
'Latitude' : latitude,
'Longitude' : longitude,
})
df_counters.head()
locations = df_counters[['Latitude', 'Longitude']]
locationlist = locations.values.tolist()
map3 = folium.Map(location=[31.1893,121.2781], tiles='CartoDB positron', zoom_start=6)
marker_cluster = folium.plugins.MarkerCluster().add_to(map3)
for point in range(0, len(locationlist)):
popup=folium.Popup(df_counters['Name'][point], max_width=300,min_width=300)
folium.Marker(locationlist[point],
popup=popup,
icon=folium.Icon(color='blue', icon_color='white',
icon='fa-shopping-bag', angle=0, prefix='fa')
).add_to(marker_cluster)
map3.save("WorldMap.html")`
Right now I have 4 other columns in my XML file besides 'Name' that have the information that I want to appear in the popup as well, kinda like this :
example popup
Thank you for your help
Edit :
I did some digging and changed my code a little bit by adding the folium.features.GeoJsonPopup instead of the simple folium.Popup that I had before :
for point in range(0, len(locationlist)):
popup=folium.features.GeoJsonPopup(
fields=[['Name'],['Opening']],
aliases=['Name','Opening'])
folium.Marker(locationlist[point],
popup=popup,
icon=folium.Icon(color='blue', icon_color='white',
icon='fa-shopping-bag', angle=0, prefix='fa')
).add_to(marker_cluster)
I added the 'Opening' data, however I don't know how to transfer it into the pop up along with the 'Name' since it comes from a panda DataFrame. Right now my popups are empty.
I have done something similar, steps were:
create an IFrame with the content you want to display (coded in HTML)
use this IFrame in a popup
connect this popup with your marker
htmlstr = ... # Here you can add your table, use HTML
# 1. iframe
iframe = folium.IFrame(htmlstr, # places your content in the iframe
width=200,
height=200 # adjust size to your needs
)
# 2. popup
fpop = folium.Popup(iframe)
# 3. marker
mrk = folium.Marker(location=latlng,
popup=fpop,
)
mrk.add_to( ... )

Setting the background color of a table cell in Word [docx] [Python 3.8.2]

I'm having problems understanding how to set the background color of a table cell.
I tried many combinations, but the only thing I managed to do was the to set the paragraph style.
import docx
doc = docx.Document()
records = (
(3, '101', 'Spam'),
(7, '422', 'Eggs'),
(4, '631', 'Spam, spam, eggs, and spam')
)
table = doc.add_table(rows=1, cols=3)
hdr_cells = table.rows[0].cells
hdr_cells[0].text = 'Qty'
hdr_cells[1].text = 'Id'
hdr_cells[2].text = 'Desc'
for qty, id, desc in records:
row_cells = table.add_row().cells
row_cells[0].add_paragraph(str(qty), "Title 1")
row_cells[0].text_frame.paragraph[0].font.size = Pt(12)
row_cells[1].text = id
row_cells[2].text = desc
doc.save("Testing.docx")
I would like to set row_cells[0] to have a red background, but I'm unable to do it.
How can I do it?
That option is still not supported by the official python-docx library.
However, you can try implementing it yourself. The property you're looking for is called cell shading located under cell properties.
Solution:
Add the shading element(w:shd) to the cell properties(w:tcPr).
Wrote the simple function that does exactly that:
def _set_cell_background(cell, fill, color=None, val=None):
"""
#fill: Specifies the color to be used for the background
#color: Specifies the color to be used for any foreground
pattern specified with the val attribute
#val: Specifies the pattern to be used to lay the pattern
color over the background color.
"""
from docx.oxml.shared import qn # feel free to move these out
from docx.oxml.xmlchemy import OxmlElement
cell_properties = cell._element.tcPr
try:
cell_shading = cell_properties.xpath('w:shd')[0] # in case there's already shading
except IndexError:
cell_shading = OxmlElement('w:shd') # add new w:shd element to it
if fill:
cell_shading.set(qn('w:fill'), fill) # set fill property, respecting namespace
if color:
pass # TODO
if val:
pass # TODO
cell_properties.append(cell_shading) # finally extend cell props with shading element
Feel free to extend other properties if you need em.
So based on your example, once you have your table, before saving the doc, add this line:
.
.
_set_cell_background(table.rows[0].cells[0], 'FF0000')
doc.save("Testing.docx")
Also, feel free to contribute to the official library by registering this new element here: https://github.com/python-openxml/python-docx/blob/master/docx/oxml/table.py#L753. :)
Hope this helps,

How to edit editable pdf using the pdfrw library?

I been doing research on how to edit PDF using Python and i have found this article:
How to Populate Fillable PDF's with Python
However there is a problem once the program runs and you open the PDF the document is not populated only when you click on the tags it shows the data and when you click away it disappears again. This is code that can be found online that someone else has written.
#! /usr/bin/python
import os
import pdfrw
INVOICE_TEMPLATE_PATH = 'invoice_template.pdf'
INVOICE_OUTPUT_PATH = 'invoice.pdf'
ANNOT_KEY = '/Annots'
ANNOT_FIELD_KEY = '/T'
ANNOT_VAL_KEY = '/V'
ANNOT_RECT_KEY = '/Rect'
SUBTYPE_KEY = '/Subtype'
WIDGET_SUBTYPE_KEY = '/Widget'
def write_fillable_pdf(input_pdf_path, output_pdf_path, data_dict):
template_pdf = pdfrw.PdfReader(input_pdf_path)
annotations = template_pdf.pages[0][ANNOT_KEY]
for annotation in annotations:
if annotation[SUBTYPE_KEY] == WIDGET_SUBTYPE_KEY:
if annotation[ANNOT_FIELD_KEY]:
key = annotation[ANNOT_FIELD_KEY][1:-1]
if key in data_dict.keys():
annotation.update(
pdfrw.PdfDict(V='{}'.format(data_dict[key]))
)
pdfrw.PdfWriter().write(output_pdf_path, template_pdf)
data_dict = {
'business_name_1': 'Bostata',
'customer_name': 'company.io',
'customer_email': 'joe#company.io',
'invoice_number': '102394',
'send_date': '2018-02-13',
'due_date': '2018-03-13',
'note_contents': 'Thank you for your business, Joe',
'item_1': 'Data consulting services',
'item_1_quantity': '10 hours',
'item_1_price': '$200/hr',
'item_1_amount': '$2000',
'subtotal': '$2000',
'tax': '0',
'discounts': '0',
'total': '$2000',
'business_name_2': 'Bostata LLC',
'business_email_address': 'hi#bostata.com',
'business_phone_number': '(617) 930-4294'
}
if __name__ == '__main__':
write_fillable_pdf(INVOICE_TEMPLATE_PATH, INVOICE_OUTPUT_PATH, data_dict)
I figure out that if you add NeedAppearances param you will solve your problem:
template_pdf = pdfrw.PdfReader(TEMPLATE_PATH)
template_pdf.Root.AcroForm.update(pdfrw.PdfDict(NeedAppearances=pdfrw.PdfObject('true')))
Updating the write function to have keys AP and V fixed the problem for me in preview
pdfrw.PdfDict(AP=data_dict[key], V=data_dict[key])
The error is because no appearance stream is associated with the field, but you've created it in a wrong way. You've just assigned and stream to AP dictionary. What you need to do is to assign an indirect Xobject to /N in /AP dictionary; and you need to crate Xobject from scratch.
The code should be something like the following:
from pdfrw import PdfWriter, PdfReader, IndirectPdfDict, PdfName, PdfDict
INVOICE_TEMPLATE_PATH = 'untitled.pdf'
INVOICE_OUTPUT_PATH = 'untitled-output.pdf'
field1value = 'im field_1 value'
template_pdf = PdfReader(INVOICE_TEMPLATE_PATH)
template_pdf.Root.AcroForm.Fields[0].V = field1value
#this depends on page orientation
rct = template_pdf.Root.AcroForm.Fields[0].Rect
hight = round(float(rct[3]) - float(rct[1]),2)
width =(round(float(rct[2]) - float(rct[0]),2)
#create Xobject
xobj = IndirectPdfDict(
BBox = [0, 0, width, hight],
FormType = 1,
Resources = PdfDict(ProcSet = [PdfName.PDF, PdfName.Text]),
Subtype = PdfName.Form,
Type = PdfName.XObject
)
#assign a stream to it
xobj.stream = '''/Tx BMC
BT
/Helvetica 8.0 Tf
1.0 5.0 Td
0 g
(''' + field1value + ''') Tj
ET EMC'''
#put all together
template_pdf.Root.AcroForm.Fields[0].AP = PdfDict(N = xobj)
#output to new file
PdfWriter().write(INVOICE_OUTPUT_PATH, template_pdf)
Note: FYI: /Type, /FormType, /Resorces are optional (/Resources is strongly recomended).
To expand on Sergio's answer above, the following line:
template_pdf.Root.AcroForm.update(pdfrw.PdfDict(NeedAppearances=pdfrw.PdfObject('true')))
Should be put after this line in the example code from OP:
template_pdf = pdfrw.PdfReader(input_pdf_path)
In case someone has dropdown fields on the form you want to populate with data you can use the code below. (Might save someone the hassle I went through)
if key in data_dict.keys():
#see if its a dropdown
if('/I' in annotation.keys()):
#field is a dropdown
#Check if value is in preset list of dropdown, and at what value
if data_dict[key] in annotation['/Opt']:
#Value is in dropdown list,select value from list
annotation.update(pdfrw.PdfDict(I='[{}]'.format(annotation['/Opt'].index(data_dict[key]))))
else:
#Value is not in dropdown list, add as 'free input'
annotation.update(pdfrw.PdfDict(I='{}'.format(None)))
annotation.update(pdfrw.PdfDict(V='{}'.format(data_dict[key])))
else:
#update the textfieldvalue
annotation.update(pdfrw.PdfDict(V='{}'.format(data_dict[key])))
also not that the OP code only works for the first page due to
template_pdf.pages[0]

Display and update text with Bokeh: settings glyph on update not working

I am trying to write a web app that displays graphs with Bokeh and also displays formatted text retrieved from an API. The display dynamically changes with the content of the graph when the user clicks a button. Is there a standard way to format and present text, and then update it, with Bokeh?
I thought I could do this with text glyphs, but so far I am failing. Here's what I'm trying to do for the update action:
def update_graph(attrname, old, new):
USER_ID = new
use_df = get_user_df(new, cur, date_from, date_to)
plot1.title = get_api_output(new, INSIGHT_DATE)
textplot.text(3, 3, text=["hellllllo"], text_color="firebrick", text_align="center", text_font_size="20pt")
source1.data['x'] = use_df[use_df['type'] == 0]['date'].values
source1.data['y'] = use_df[use_df['type'] == 0]['value'].values
This mostly works fine the way it is. I grab some data from a remote DB with get_user_df and update source1, which works appropriately. Similarly the title gets updated as desired.
However no text glyph appears on textplot and no error appears in the console. What's going on and how can I dynamically adjust text glyphs on a plot?
Also here is the code I use to set up the plots in case that is relevant:
#plot utilities
def mtext(p, x, y, text):
p.text(x, y, text=[text],
text_color="firebrick", text_align="center", text_font_size="10pt")
#PLOTS
textplot = Figure(toolbar_location = None, title = 'API text', title_text_font_size='8pt')
mtext(textplot, randint(2, 5), randint(2, 5), 'annotation')
plot1 = Figure(toolbar_location = None, title = 'Distance (k)', title_text_font_size='8pt')
source1 = ColumnDataSource(data = dict(x = use_df[use_df['type'] == 0]['date'].values, y = use_df[use_df['type'] == 0]['value'].values))
plot1.circle('x', 'y', line_color=None, source = source1, color = 'pink', size = 20)

Bokeh - get information about points that have been selected

I have a several points that I plot into scatter plot and show in web browser window (using Bokeh).
For selection, I use PolySelectTool or BoxSelectTool.
There are two things I would like to do:
1) Get information about points that have been selected in order to calculate some additional information.
2) As points represent URLs, I would like the chart to open a new browser tab and load a particular URL whenever I click a point (representing URL).
I don't think the code is important. But to make my question complete, here it is ...
Y = my_data
urls = get_urls(my_data)
TOOLS="pan,wheel_zoom,reset,hover,poly_select,box_select"
p = figure(title = "My chart", tools=TOOLS)
p.xaxis.axis_label = 'X'
p.yaxis.axis_label = 'Y'
source = ColumnDataSource(
data=dict(
xvals=list(Y[:,0]),
yvals=list(Y[:,1]),
url=urls
)
)
p.scatter("xvals", "yvals",source=source,fill_alpha=0.2, size=5)
hover = p.select(dict(type=HoverTool))
hover.snap_to_data = False
hover.tooltips = OrderedDict([
("(x,y)", "($x, $y)"),
("url", "#url"),
])
select_tool = p.select(dict(type=BoxSelectTool))
#
# I guess perhaps something should be done with select_tool
#
show(p)
You can get information with the source.selected property, if you want to be notified of every change you must create a callback, it would be something like this:
def callback(obj, attr, old, new):
...
source.on_change('selected', callback)
See this example for more details.

Categories