Skip to content
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
28 changes: 26 additions & 2 deletions folium/plugins/draw.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,25 @@
from folium.elements import JSCSSMixin
from folium.template import Template

# Each corner maps to the pair of CSS edge offsets that pins the export button
# there. "topright" reproduces the historical hard-coded placement exactly.
_EXPORT_POSITION_CSS = {
"topright": "top: 90px;\n right: 10px;",
"topleft": "top: 90px;\n left: 10px;",
"bottomright": "bottom: 20px;\n right: 10px;",
"bottomleft": "bottom: 20px;\n left: 10px;",
}


def _export_position_to_css(export_position):
try:
return _EXPORT_POSITION_CSS[export_position]
except KeyError:
raise ValueError(
"export_position must be one of "
f"{sorted(_EXPORT_POSITION_CSS)}, not {export_position!r}"
) from None


class Draw(JSCSSMixin, MacroElement):
'''
Expand All @@ -20,6 +39,10 @@ class Draw(JSCSSMixin, MacroElement):
position : {'topleft', 'toprigth', 'bottomleft', 'bottomright'}
Position of control.
See https://leafletjs.com/reference.html#control
export_position : {'topright', 'topleft', 'bottomright', 'bottomleft'}
Corner of the map to place the export button in, when ``export``
is True. Defaults to 'topright'. Use this to keep the button clear
of other controls such as a LayerControl.
show_geometry_on_click : bool, default True
When True, opens an alert with the geometry description on click.
draw_options : dict, optional
Expand Down Expand Up @@ -60,7 +83,7 @@ class Draw(JSCSSMixin, MacroElement):
<style>
#export_{{ this.get_name() }} {
position: absolute;
right: 10px;
{{ this.export_position_css }}
z-index: 999;
background: white;
color: black;
Expand All @@ -70,7 +93,6 @@ class Draw(JSCSSMixin, MacroElement):
cursor: pointer;
font-size: 12px;
text-decoration: none;
top: 90px;
}
</style>
<a href='#' id='export_{{ this.get_name() }}'>Export</a>
Expand Down Expand Up @@ -156,6 +178,7 @@ def __init__(
feature_group=None,
filename="data.geojson",
position="topleft",
export_position="topright",
show_geometry_on_click=True,
draw_options=None,
edit_options=None,
Expand All @@ -167,6 +190,7 @@ def __init__(
self.feature_group = feature_group
self.filename = filename
self.position = position
self.export_position_css = _export_position_to_css(export_position)
self.show_geometry_on_click = show_geometry_on_click
self.draw_options = draw_options or {}
self.edit_options = edit_options or {}
Expand Down
41 changes: 41 additions & 0 deletions tests/plugins/test_draw.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@

import re

import pytest

import folium
from folium import plugins
from folium.template import Template
Expand Down Expand Up @@ -68,3 +70,42 @@ def test_two_draw_controls_get_unique_export_ids():
handler = out[start : out.index("}", start) + 1]
assert f"drawnItems_{draw.get_name()}.toGeoJSON()" in handler
assert filename in handler


def test_draw_export_position_default():
"""The default export button keeps its historical top-right placement."""
m = folium.Map([45.0, 3.0], zoom_start=4)
draw = plugins.Draw(export=True)
m.add_child(draw)

out = normalize(m._parent.render())

block = out[out.index(f"#export_{draw.get_name()}") :]
block = block[: block.index("}")]
assert "position: absolute;" in block
assert "top: 90px;" in block
assert "right: 10px;" in block


def test_draw_export_position_corners():
expected = {
"topright": ("top: 90px;", "right: 10px;"),
"topleft": ("top: 90px;", "left: 10px;"),
"bottomright": ("bottom: 20px;", "right: 10px;"),
"bottomleft": ("bottom: 20px;", "left: 10px;"),
}
for position, edges in expected.items():
m = folium.Map([45.0, 3.0], zoom_start=4)
draw = plugins.Draw(export=True, export_position=position)
m.add_child(draw)

out = normalize(m._parent.render())
block = out[out.index(f"#export_{draw.get_name()}") :]
block = block[: block.index("}")]
for edge in edges:
assert edge in block, f"{position}: {edge!r} missing from {block!r}"


def test_draw_export_position_invalid():
with pytest.raises(ValueError, match="export_position must be one of"):
plugins.Draw(export=True, export_position="middle")
Loading