Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

show_in_browser() method for folium.folium.Map #953

Closed
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions examples/OpenMapInBrowser.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@

"""
Uses plain python and opens the folium map in a browser window. No IPython, Jupyter or else is required.

"""

import numpy as np
import folium
from folium.plugins import HeatMap

# create a dummy map
data = (np.random.normal(size=(100, 3)) *
np.array([[1, 1, 1]]) +
np.array([[48, 5, 1]])).tolist()
folium_map = folium.Map([48., 5.], tiles='stamentoner', zoom_start=6)
HeatMap(data).add_to(folium_map)

# open in in browser
folium_map.show_in_browser()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need for this example. This can be in the method docstring.


119 changes: 119 additions & 0 deletions folium/_server.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
# -*- coding: utf-8 -*-

"""
Simple HTTP server so we are able to show Maps in browser. Only works with python 3!.

"""

import sys
if sys.version_info[0] < 3:
raise NotImplementedError('Only works with python 3!')
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe a more verbose message like:

This feature is only available on Python 3.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I actually realised i might have misunderstood something. Because there is an http server in python 2, so I'll check it again tomorrow.


import webbrowser

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

E402 module level import not at top of file

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that seems to be the case for most of the other files in the repo, so I chose consistency over purity.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

E402 module level import not at top of file

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same as before this seems to be a convention in the repo

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

E402 module level import not at top of file

from http.server import BaseHTTPRequestHandler, HTTPServer

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

E402 module level import not at top of file

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that seems to be the case for most of the other files in the repo, so I chose consistency over purity.

from textwrap import dedent

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

E402 module level import not at top of file

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that seems to be the case for most of the other files in the repo, so I chose consistency over purity.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please follow the liner cues here. We are cleaning up the code and new code should be compliant from the start.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

E402 module level import not at top of file

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same as before this seems to be a convention in the repo

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

E402 module level import not at top of file



# Based on https://stackoverflow.com/a/38945907/3494126
class TemporaryHTTPServer:
"""
A simple, temporary http web server on the pure Python 3.

Parameters
----------
host: str
Local address on which the page will be hosted, default is '127.0.0.1'
port: int
Corresponding port, default 7000
"""
def __init__(self, host=None, port=None):
self.host = host or '127.0.0.1'
self.port = port or 7000

self.server_address = '{host}:{port}'.format(host=self.host, port=self.port)
self.full_server_address = 'http://' + self.server_address

def serve(self, html_data):
"""
Serve html content in a suitable for us manner: allow to gracefully exit using ctrl+c and re-serve some other
content on the same host:port

"""

# we need a request handler with a method `do_GET` which somehow is not provided in the baseline
class HTTPServerRequestHandler(BaseHTTPRequestHandler):
"""
An handler of requests for the server, hosting HTML-pages.
"""

def do_GET(self):
"""Handle GET requests"""

# response from page
self.send_response(200)

# set up headers for pages
# content_type = 'text/{0}'.format(page_content_type)
self.send_header('Content-type', 'text/html')
self.end_headers()

# writing data on a page
self.wfile.write(bytes(html_data, encoding='utf'))

return

# create a temporary server
HTTPServer.allow_reuse_address = True
httpd = HTTPServer((self.host, self.port), HTTPServerRequestHandler)

# run the temporary http server with graceful exit option
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Most of these comment can be removed. I guess that they were added on SO to explain the answer. We don't need them here.

try:
httpd.serve_forever()
except KeyboardInterrupt:
print('\n Closing the server.')
httpd.server_close()
except Exception:
raise

def open_html_in_browser(self, html_data=None):
"""
Opens a browser window showing the html content

Parameters
----------
html_data: str
Should be a valid html code

Examples
--------

html_data = '''
<!DOCTYPE html>
<html>
<head>
<title> Test Page </title>
</head>
<body>
<p> Seems to be working. Now give the function `open_html_in_browser` some content! </p>
</body>
</html>
'''

srvr = TemporaryHTTPServer()
srvr.open_html_in_browser(html_data)

"""

# open the URL in a browser (if possible, in a new window)
webbrowser.open(self.full_server_address, new=2)

# print a user-friendly message
msg = '''
Your map is available at {link}.
It should have been opened in your browser automatically.
Press ctrl+c to return.
'''.format(link=self.full_server_address)
print(dedent(msg))

# run server (this blocks the console!)
self.serve(html_data)
21 changes: 21 additions & 0 deletions folium/folium.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import os
import tempfile
import time
import sys

from branca.colormap import StepColormap
from branca.element import CssLink, Element, Figure, JavascriptLink, MacroElement
Expand All @@ -19,6 +20,7 @@
from folium.map import FitBounds
from folium.raster_layers import TileLayer
from folium.utilities import _validate_location
from folium._server import TemporaryHTTPServer

from jinja2 import Environment, PackageLoader, Template

Expand Down Expand Up @@ -387,6 +389,25 @@ def render(self, **kwargs):

super(Map, self).render(**kwargs)

def show_in_browser(self, host='127.0.0.1', port=7000):
"""
Displays the Map in the default web browser.

Parameters
----------
host: str
Local address on which the Map will be hosted, default is '127.0.0.1'
port: int
Corresponding port, default 7000

"""

if sys.version_info[0] < 3:
raise NotImplementedError('show_in_browser only works with python 3!')

srvr = TemporaryHTTPServer(host, port)
srvr.open_html_in_browser(self._repr_html_())

def fit_bounds(self, bounds, padding_top_left=None,
padding_bottom_right=None, padding=None, max_zoom=None):
"""Fit the map to contain a bounding box with the
Expand Down
4 changes: 4 additions & 0 deletions requirements-dev.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
altair
cartopy
descartes
flake8
flake8-builtins
flake8-comprehensions
Expand All @@ -12,6 +13,7 @@ geopandas
gpxpy
ipykernel
jupyter_client
matplotlib
mplleaflet
nbconvert
nbsphinx
Expand All @@ -20,7 +22,9 @@ pillow
pycodestyle
pytest
selenium
scipy
sphinx
twine
owslib
vega_datasets
vincent