The proposed code from @Yuvraj is not working, because it still creates link, but now this link is a JS code:
127.0.0.1 - - [26/Mar/2025 16:33:59] "POST / HTTP/1.1" 200 -
127.0.0.1 - - [26/Mar/2025 16:33:59] "GET /(function%20(factory,%20window)%20{%20%20%20%20//%20define%20an%20AMD%20module%20that%20relies%20on%20'leaflet'%20%20%20%20if%20(typeof%20define%20=== HTTP/1.1" 404 -
127.0.0.1 - - [26/Mar/2025 16:33:59] "GET /.leaflet-control-measure%20{%20%20%20%20background-color:%20 HTTP/1.1" 404 -
127.0.0.1 - - [26/Mar/2025 16:34:00] "GET /(function%20(factory,%20window)%20{%20%20%20%20//%20define%20an%20AMD%20module%20that%20relies%20on%20'leaflet'%20%20%20%20if%20(typeof%20define%20=== HTTP/1.1" 404 -
127.0.0.1 - - [26/Mar/2025 16:34:00] "GET /.leaflet-control-measure%20{%20%20%20%20background-color:%20 HTTP/1.1" 404 -
But, thanks to him I've found two other options
This will still create "localhost:port/leaflet.measure.js" and "localhost:port/leaflet.measure.css" links in header of final HTML page:
from jinja2 import Template
from folium.elements import JSCSSMixin
from folium.utilities import parse_options
class Measure(JSCSSMixin):
_template = Template(
"""
{% macro script(this, kwargs) %}
var {{ this.get_name() }} = new L.control.measure(
{{ this.options|tojson }});
{{this._parent.get_name()}}.addControl({{this.get_name()}});
{% endmacro %}
"""
)
# send static file from localhost via flask
default_js = [("leaflet.measure.js", "leaflet.measure.js") ]
default_css = [("leaflet.measure.css", "leaflet.measure.css") ]
def __init__(self, embedded=True, **kwargs):
super().__init__()
self._name = "Measure"
self.options = parse_options(**kwargs)
But we will resolve them with flask static file serving:
@app.route('/leaflet.measure.js', methods=['GET'])
def foo():
return app.send_static_file('leaflet.measure.js')
@app.route('/leaflet.measure.css', methods=['GET'])
def baz():
return app.send_static_file('leaflet.measure.css')
Inject JS/CSS code directly from static files from python package, no additional requests, no flask code modifications needed:
# https://github.com/python-visualization/folium/blob/main/folium/elements.py
# https://github.com/python-visualization/branca/blob/main/branca/element.py
# https://github.com/python-visualization/folium/blob/main/folium/plugins/measure_control.py
import importlib.resources
from jinja2 import Template
from branca.element import Figure
from folium.elements import JSCSSMixin # inherits from branca.element.MacroElement
from folium.utilities import parse_options
# I heard that you like templates, now you can inject template inside template, while injecting template to main template
class TemplateInjector:
""" Class with compatible API for branca.element.Element to be added as a child to folium map
It must have render() and get_name()
"""
def __init__(self, name: str, template: Template):
self._name = name
self._template = template
def render(self, **kwargs) -> str:
return self._template.render(this=self, kwargs=kwargs)
def get_name(self) -> str:
return self._name
class Measure(JSCSSMixin):
# will be self._template for all created class objects
# see MacroElement in <https://github.com/python-visualization/branca/blob/main/branca/element.py>
_template = Template(
"""
{% macro script(this, kwargs) %}
var {{ this.get_name() }} = new L.control.measure(
{{ this.options|tojson }});
{{this._parent.get_name()}}.addControl({{this.get_name()}});
{% endmacro %}
"""
)
# reload JSCSSMixin method
def render(self, **kwargs):
figure = self.get_root()
assert isinstance(figure, Figure), "You cannot render this Element if it is not in a Figure."
# load JS code and push it into template
leaflet_measure_js = importlib.resources.files('geo_plotter').joinpath("static/leaflet.measure.js").read_text()
js_template = Template(f"<script>{leaflet_measure_js}</script>")
# push template into header
figure.header.add_child(TemplateInjector("leaflet_measure_js", js_template))
# load CSS code and push it into template
leaflet_measure_css = importlib.resources.files('geo_plotter').joinpath("static/leaflet.measure.css").read_text()
css_template = Template(f"<style>{leaflet_measure_css}</style>")
# push template into header
figure.header.add_child(TemplateInjector("leaflet_measure_css", css_template))
super().render(**kwargs)
def __init__(self, embedded=True, **kwargs):
super().__init__()
self._name = "Measure"
self.options = parse_options(**kwargs)