Skip to content

Cleanup MacroElements #2088

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

Merged
merged 2 commits into from
Mar 22, 2025
Merged
Show file tree
Hide file tree
Changes from all 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
4 changes: 4 additions & 0 deletions folium/elements.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ class JSCSSMixin(MacroElement):
default_js: List[Tuple[str, str]] = []
default_css: List[Tuple[str, str]] = []

# Since this is typically used as a mixin, we cannot
# override the _template member variable here. It would
# be overwritten by any subclassing class that also has
# a _template variable.
def render(self, **kwargs):
figure = self.get_root()
assert isinstance(
Expand Down
66 changes: 25 additions & 41 deletions folium/folium.py
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,29 @@ class Map(JSCSSMixin, Evented):
}
.leaflet-container { font-size: {{this.font_size}}; }
</style>

<style>html, body {
width: 100%;
height: 100%;
margin: 0;
padding: 0;
}
</style>

<style>#map {
position:absolute;
top:0;
bottom:0;
right:0;
left:0;
}
</style>

<script>
L_NO_TOUCH = {{ this.global_switches.no_touch |tojson}};
L_DISABLE_3D = {{ this.global_switches.disable_3d|tojson }};
</script>

{% endmacro %}

{% macro html(this, kwargs) %}
Expand Down Expand Up @@ -304,6 +327,8 @@ def __init__(
else:
self.zoom_control_position = False

self.global_switches = GlobalSwitches(no_touch, disable_3d)

self.options = remove_empty(
max_bounds=max_bounds_array,
zoom=zoom_start,
Expand All @@ -312,8 +337,6 @@ def __init__(
**kwargs,
)

self.global_switches = GlobalSwitches(no_touch, disable_3d)

self.objects_to_stay_in_front: List[Layer] = []

if isinstance(tiles, TileLayer):
Expand Down Expand Up @@ -377,45 +400,6 @@ def _repr_png_(self) -> Optional[bytes]:
return None
return self._to_png()

def render(self, **kwargs):
"""Renders the HTML representation of the element."""
figure = self.get_root()
assert isinstance(
figure, Figure
), "You cannot render this Element if it is not in a Figure."

# Set global switches
figure.header.add_child(self.global_switches, name="global_switches")

figure.header.add_child(
Element(
"<style>html, body {"
"width: 100%;"
"height: 100%;"
"margin: 0;"
"padding: 0;"
"}"
"</style>"
),
name="css_style",
)

figure.header.add_child(
Element(
"<style>#map {"
"position:absolute;"
"top:0;"
"bottom:0;"
"right:0;"
"left:0;"
"}"
"</style>"
),
name="map_style",
)

super().render(**kwargs)

def show_in_browser(self) -> None:
"""Display the Map in the default web browser."""
with temp_html_filepath(self.get_root().render()) as fname:
Expand Down
57 changes: 24 additions & 33 deletions folium/plugins/draw.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from branca.element import Element, Figure, MacroElement
from branca.element import MacroElement

from folium.elements import JSCSSMixin
from folium.template import Template
Expand Down Expand Up @@ -60,6 +60,29 @@ class Draw(JSCSSMixin, MacroElement):

_template = Template(
"""
{% macro html(this, kwargs) %}
{% if this.export %}
<style>
Copy link
Member

Choose a reason for hiding this comment

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

this style element was originally added to the header. Here it's in the body. I guess it doesn't make a difference really, does it?

#export {
position: absolute;
top: 5px;
right: 10px;
z-index: 999;
background: white;
color: black;
padding: 6px;
border-radius: 4px;
font-family: 'Helvetica Neue';
cursor: pointer;
font-size: 12px;
text-decoration: none;
top: 90px;
}
</style>
<a href='#' id='export'>Export</a>
{% endif %}
{% endmacro %}

{% macro script(this, kwargs) %}
var options = {
position: {{ this.position|tojson }},
Expand Down Expand Up @@ -155,35 +178,3 @@ def __init__(
self.draw_options = draw_options or {}
self.edit_options = edit_options or {}
self.on = on or {}

def render(self, **kwargs):
super().render(**kwargs)

figure = self.get_root()
assert isinstance(
figure, Figure
), "You cannot render this Element if it is not in a Figure."

export_style = """
<style>
#export {
position: absolute;
top: 5px;
right: 10px;
z-index: 999;
background: white;
color: black;
padding: 6px;
border-radius: 4px;
font-family: 'Helvetica Neue';
cursor: pointer;
font-size: 12px;
text-decoration: none;
top: 90px;
}
</style>
"""
export_button = """<a href='#' id='export'>Export</a>"""
if self.export:
figure.header.add_child(Element(export_style), name="export")
figure.html.add_child(Element(export_button), name="export_button")
178 changes: 81 additions & 97 deletions folium/plugins/heat_map_withtime.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
from branca.element import Element, Figure

from folium.elements import JSCSSMixin
from folium.map import Layer
from folium.template import Template
Expand Down Expand Up @@ -61,6 +59,87 @@ class HeatMapWithTime(JSCSSMixin, Layer):

_template = Template(
"""
{% macro header(this, kwargs) %}
<script>
var TDHeatmap = L.TimeDimension.Layer.extend({
initialize: function(data, options) {
var heatmapCfg = {
radius: 15,
blur: 0.8,
maxOpacity: 1.,
scaleRadius: false,
useLocalExtrema: false,
latField: 'lat',
lngField: 'lng',
valueField: 'count',
defaultWeight : 1,
};
heatmapCfg = $.extend({}, heatmapCfg, options.heatmapOptions || {});
var layer = new HeatmapOverlay(heatmapCfg);
L.TimeDimension.Layer.prototype.initialize.call(this, layer, options);
this._currentLoadedTime = 0;
this._currentTimeData = {
data: []
};
this.data= data;
this.defaultWeight = heatmapCfg.defaultWeight || 1;
},
onAdd: function(map) {
L.TimeDimension.Layer.prototype.onAdd.call(this, map);
map.addLayer(this._baseLayer);
if (this._timeDimension) {
this._getDataForTime(this._timeDimension.getCurrentTime());
}
},
_onNewTimeLoading: function(ev) {
this._getDataForTime(ev.time);
return;
},
isReady: function(time) {
return (this._currentLoadedTime == time);
},
_update: function() {
this._baseLayer.setData(this._currentTimeData);
return true;
},
_getDataForTime: function(time) {
delete this._currentTimeData.data;
this._currentTimeData.data = [];
var data = this.data[time-1];
for (var i = 0; i < data.length; i++) {
this._currentTimeData.data.push({
lat: data[i][0],
lng: data[i][1],
count: data[i].length>2 ? data[i][2] : this.defaultWeight
});
}
this._currentLoadedTime = time;
if (this._timeDimension && time == this._timeDimension.getCurrentTime() && !this._timeDimension.isLoading()) {
this._update();
}
this.fire('timeload', {
time: time
});
}
});

L.Control.TimeDimensionCustom = L.Control.TimeDimension.extend({
initialize: function(index, options) {
var playerOptions = {
buffer: 1,
minBufferReady: -1
};
options.playerOptions = $.extend({}, playerOptions, options.playerOptions || {});
L.Control.TimeDimension.prototype.initialize.call(this, options);
this.index = index;
},
_getDisplayDateFormat: function(date) {
return this.index[date.getTime()-1];
}
});
</script>
{% endmacro %}

{% macro script(this, kwargs) %}

var times = {{this.times}};
Expand Down Expand Up @@ -202,101 +281,6 @@ def __init__(
self.time_slider_drag_update = "false"
self.style_NS = "leaflet-control-timecontrol"

def render(self, **kwargs):
super().render(**kwargs)

figure = self.get_root()
assert isinstance(
figure, Figure
), "You cannot render this Element if it is not in a Figure."

figure.header.add_child(
Element(
"""
<script>
var TDHeatmap = L.TimeDimension.Layer.extend({

initialize: function(data, options) {
var heatmapCfg = {
radius: 15,
blur: 0.8,
maxOpacity: 1.,
scaleRadius: false,
useLocalExtrema: false,
latField: 'lat',
lngField: 'lng',
valueField: 'count',
defaultWeight : 1,
};
heatmapCfg = $.extend({}, heatmapCfg, options.heatmapOptions || {});
var layer = new HeatmapOverlay(heatmapCfg);
L.TimeDimension.Layer.prototype.initialize.call(this, layer, options);
this._currentLoadedTime = 0;
this._currentTimeData = {
data: []
};
this.data= data;
this.defaultWeight = heatmapCfg.defaultWeight || 1;
},
onAdd: function(map) {
L.TimeDimension.Layer.prototype.onAdd.call(this, map);
map.addLayer(this._baseLayer);
if (this._timeDimension) {
this._getDataForTime(this._timeDimension.getCurrentTime());
}
},
_onNewTimeLoading: function(ev) {
this._getDataForTime(ev.time);
return;
},
isReady: function(time) {
return (this._currentLoadedTime == time);
},
_update: function() {
this._baseLayer.setData(this._currentTimeData);
return true;
},
_getDataForTime: function(time) {
delete this._currentTimeData.data;
this._currentTimeData.data = [];
var data = this.data[time-1];
for (var i = 0; i < data.length; i++) {
this._currentTimeData.data.push({
lat: data[i][0],
lng: data[i][1],
count: data[i].length>2 ? data[i][2] : this.defaultWeight
});
}
this._currentLoadedTime = time;
if (this._timeDimension && time == this._timeDimension.getCurrentTime() && !this._timeDimension.isLoading()) {
this._update();
}
this.fire('timeload', {
time: time
});
}
});

L.Control.TimeDimensionCustom = L.Control.TimeDimension.extend({
initialize: function(index, options) {
var playerOptions = {
buffer: 1,
minBufferReady: -1
};
options.playerOptions = $.extend({}, playerOptions, options.playerOptions || {});
L.Control.TimeDimension.prototype.initialize.call(this, options);
this.index = index;
},
_getDisplayDateFormat: function(date){
return this.index[date.getTime()-1];
}
});
</script>
""", # noqa
template_name="timeControlScript",
)
)

def _get_self_bounds(self):
"""
Computes the bounds of the object itself (not including it's children)
Expand Down
2 changes: 1 addition & 1 deletion folium/plugins/search.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
from branca.element import MacroElement

from folium import Map
from folium.elements import JSCSSMixin
from folium.features import FeatureGroup, GeoJson, TopoJson
from folium.folium import Map
from folium.plugins import MarkerCluster
from folium.template import Template
from folium.utilities import remove_empty
Expand Down
Loading