From dcee898085f87a6889530144279029f479819d3e Mon Sep 17 00:00:00 2001 From: KhadeerBasha1232 Date: Wed, 4 Nov 2026 18:31:06 +0530 Subject: [PATCH] Keep natural image size for CustomIcon without icon_size Leaflet reuses the default marker's when setIcon swaps in another image icon, and keeps its inline width, height and margins when the new icon does not set them. A CustomIcon without icon_size was therefore shown at the default icon's 25x41 px. Clear those styles before calling setIcon. Closes #1934 Co-Authored-By: Claude Opus 5.5 --- folium/features.py | 3 ++- folium/map.py | 13 +++++++++++++ tests/test_map.py | 18 ++++++++++++++++++ 3 files changed, 33 insertions(+), 1 deletion(-) diff --git a/folium/features.py b/folium/features.py index 587f5e053b..2a38e74c57 100644 --- a/folium/features.py +++ b/folium/features.py @@ -1888,7 +1888,8 @@ class CustomIcon(Icon): * Otherwise a string will be assumed to be JSON and embedded. * If array-like, it will be converted to PNG base64 string and embedded. icon_size : tuple of 2 int, optional - Size of the icon image in pixels. + Size of the icon image in pixels. If not given, the image is shown + at its natural size. icon_anchor : tuple of 2 int, optional The coordinates of the "tip" of the icon (relative to its top left corner). diff --git a/folium/map.py b/folium/map.py index daf7ee84cb..7f08da9884 100644 --- a/folium/map.py +++ b/folium/map.py @@ -509,8 +509,21 @@ class Marker(MacroElement): class SetIcon(MacroElement): """Set the icon of a marker after both are created.""" + # Leaflet reuses the current element when the new icon is also an + # image, and keeps its inline size and margins if the new icon does not + # set them. Clear them first, so an icon without `icon_size` keeps the + # natural size of its image instead of the size of the default icon. _template = Template(""" {% macro script(this, kwargs) %} + { + const element = {{ this.marker.get_name() }}.getElement(); + if (element) { + element.style.width = ""; + element.style.height = ""; + element.style.marginLeft = ""; + element.style.marginTop = ""; + } + } {{ this.marker.get_name() }}.setIcon({{ this.icon.get_name() }}); {% endmacro %} """) diff --git a/tests/test_map.py b/tests/test_map.py index 594c42ce0e..1cebf9c2fb 100644 --- a/tests/test_map.py +++ b/tests/test_map.py @@ -305,6 +305,24 @@ def test_marker_numpy_array_as_location(): Marker(np.array([0, 0])) +def test_set_icon_clears_size_of_previous_icon(): + # Leaflet reuses the of the default icon, so its inline size must be + # cleared for a custom icon without icon_size to keep its natural size. + from folium.features import CustomIcon + + m = Map() + marker = Marker([0, 0], icon=CustomIcon("https://example.com/logo.png")) + marker.add_to(m) + script = normalize(m.get_root().render()) + element = f"{marker.get_name()}.getElement()" + set_icon = f"{marker.get_name()}.setIcon({marker.icon.get_name()});" + assert element in script + assert set_icon in script + assert script.index(element) < script.index(set_icon) + for prop in ("width", "height", "marginLeft", "marginTop"): + assert f'.style.{prop} = "";' in script + + def test_icon_hex_marker_color(): with warnings.catch_warnings(): warnings.simplefilter("error")