I've been trying to add an image to a folium marker popup using the following command:
html = '<img src="image.png" style="width:400px;height:300px;">'
folium.Marker(location=(lat, lon), popup=folium.Popup(folium.element.IFrame(html=html, width=420, height=320), max_width=2000)).add_to(map)
Each time I open the saved "map.html" file in the browser and click on the marker, however, the popup shows an empty white rectangle without the image.
I even tried serving the file using "python3 -m http.server 8080" and navigating to the address "127.0.0.1:8080/map.html", but I still see just the empty white rectangle.
Note that the file "image.png" is in the same directory as the "map.html" file.
Could you please let me know what I might be doing wrong? I've now been stuck with this for a few hours.
Thanks!
Your safest bet is to use base64 to encode the PNG and ensure it will be displayed in the Jupyter notebook. Here is an example: http://nbviewer.jupyter.org/gist/ocefpaf/992b4790ced1a740b1cb075ca814c2b9
But I admit that the situation could be improved.
Works! Thanks so much for this.
Quick followup to this. Would encoding using base64 be necessary even when trying to display non-raster image formats (say SVG) on a popup?
Had no idea I could do this. Thanks again. Really appreciate your help with this!
@ocefpaf
I attempted to replicate these steps for locally saved (in same directory as the "map.html) jpg photos. Clicking on the points opens a white rectangle popup but displays a 'broken link' symbol when opening with Chrome.
I followed your process from the following line (your Jupyter cell 4) onward:
encoded = base64.b64encode(open(png, 'rb').read())
But in my case, instead of 'png' I read the filename of my jpg.
The code behind the broken link reads:
"data:image/jpg;base64,b'/9j/4AAQSkZJRgAB....."
Any ideas what I might be doing wrong? Thanks.
Can you share a sscce?
@ocefpaf
Sure. Here is a simplified version of my code (apologies if this is not ideal sscce format - I tried). Simplified in that I have replaced the part at the start with arbitrary inputs where in the full version lat/lon/etc are read from photo exif data. Same applies to values of width/height/res in the lower section for now. To run this you will just need three .jpg's saved in the current directory.
For me, this generates the html map with 3 points in the correct position, but clicking them shows a white rectangle with a broken link (and also causes the map to pan away from the points).
import pandas
import os
import folium
from folium import IFrame
import base64
filelist = []
for photos in os.listdir(os.curdir):
if photos.endswith(".jpg"):
filelist.append(photos)
latlist = [51.50,51.51,51.50]
lonlist = [-0.01,0,0.01]
index = range(1,len(filelist)+1)
columns = ['Filename','Lat','Lon']
df = pandas.DataFrame(index=index, columns=columns)
df['Filename']=filelist
df['Lat']=latlist
df['Lon']=lonlist
#Create map object:
map=folium.Map(location=[df['Lat'].mean(),df['Lon'].mean()],zoom_start=13,tiles='Stamen Terrain')
for lat,lon,Filename in zip(df['Lat'],df['Lon'],df['Filename']):
encoded = base64.b64encode(open(Filename, 'rb').read())
html = '<img src="data:image/jpg;base64,{}">'.format
resolution, width, height = 75, 50, 25
iframe = IFrame(html(encoded), width=(width*resolution)+20, height=(height*resolution)+20)
popup = folium.Popup(iframe, max_width=1000)
icon = folium.Icon(color="red", icon="ok")
marker = folium.Marker(location=[lat, lon], popup=popup, icon=icon)
marker.add_to(map)
map.save(outfile='TestMap.html')
EDIT: So it was just the specified width/height parameters which was causing the panning when clicking on the point (popup was too big for screen). Reducing these solves that. But I still haven't solved the broken link.
The line '<img src="data:image/jpg;base64,{}">'.format must be '<img src="data:image/jpeg;base64,{}">'.format (note the jpeg vs jpg).
See http://nbviewer.jupyter.org/gist/ocefpaf/0ec5c93138744e5072847822818b4362
That ( plus the _.decode()_ ) did the trick! Thank you very much.
The decode should be needed only on Python 3 BTW.
.decode()
So basically it looks like this:
Filename ='abc.jpg'
encoded = base64.b64encode(open(Filename, 'rb').read())
html='《img src="data:image/jpeg;base64,{}"》'.format #replace 《》with <>
resolution, width, height = 75, 50, 25
iframe = IFrame(html(encoded.decode('UTF-8')), width=(widthresolution)+20, height=(heightresolution)+20)
popup = folium.Popup(iframe, max_width=1000)
icon = folium.Icon(color="red", icon="ok")
marker = folium.Marker(location=[37.426, -122.085], popup=popup, icon=icon)
marker.add_to(m)
@ChenghaoChenCPP
Thanks a lot! .decode('UTF-8') solved my issue!
Thanks again.
Most helpful comment
That ( plus the _.decode()_ ) did the trick! Thank you very much.