Skip to content

Commit 51da98f

Browse files
authored
Fix HeatMap crash on integer numpy weight arrays (#2244)
HeatMap's docstring documents numpy arrays of shape (n, 2) or (n, 3) as valid input, but an all-integer array (dtype int64) crashes at render time with "Object of type int64 is not JSON serializable". validate_location coerces the lat/lon columns to Python float, while the weight column (line[2:]) was passed through unchanged. np.float64 is a subclass of float so json.dumps tolerates float weights, but np.int64 is not a subclass of int, so integer weights fail. This is common in practice, e.g. HeatMap(df[["lat", "lon", "count"]].values) where the count column has an integer dtype. Coerce the weight column to float, mirroring the lat/lon normalization.
1 parent 69e7ada commit 51da98f

2 files changed

Lines changed: 42 additions & 1 deletion

File tree

folium/plugins/heat_map.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,8 @@ def __init__(
7979
self._name = "HeatMap"
8080
data = if_pandas_df_convert_to_numpy(data)
8181
self.data = [
82-
[*validate_location(line[:2]), *line[2:]] for line in data # noqa: E999
82+
[*validate_location(line[:2]), *[float(w) for w in line[2:]]]
83+
for line in data # noqa: E999
8384
]
8485
if np.any(np.isnan(self.data)):
8586
raise ValueError("data may not contain NaNs.")

tests/plugins/test_heat_map.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,3 +66,43 @@ def test_heat_map_exception():
6666
HeatMap(np.array([[4, 5, 1], [3, 6, np.nan]]))
6767
with pytest.raises(Exception):
6868
HeatMap(np.array([3, 4, 5]))
69+
70+
71+
def test_heatmap_integer_numpy_weights():
72+
"""Integer numpy arrays of shape (n, 3) are documented as supported input.
73+
74+
``np.float64`` is a subclass of ``float`` so JSON serialization tolerates
75+
float weights, but ``np.int64`` is not a subclass of ``int``, so an
76+
all-integer array (dtype ``int64``) used to crash rendering with
77+
"Object of type int64 is not JSON serializable". The weight column must be
78+
coerced to ``float`` the same way ``validate_location`` coerces lat/lon.
79+
"""
80+
data = np.array([[3, 4, 1], [5, 6, 2]])
81+
assert data.dtype == np.int64
82+
83+
hm = HeatMap(data)
84+
85+
# Weights must be normalized to plain Python floats, matching lat/lon.
86+
for point in hm.data:
87+
assert len(point) == 3
88+
for value in point:
89+
assert type(value) is float
90+
91+
# Rendering must not raise (the JSON serialization used to fail here).
92+
m = folium.Map()
93+
hm.add_to(m)
94+
out = m.get_root().render()
95+
assert "L.heatLayer" in out
96+
97+
# Integer and float weights must produce identical serialized data.
98+
hm_float = HeatMap(np.array([[3, 4, 1.0], [5, 6, 2.0]]))
99+
assert hm.data == hm_float.data
100+
101+
102+
def test_heatmap_integer_numpy_no_weight():
103+
"""Integer numpy arrays of shape (n, 2) (no weight column) also render."""
104+
data = np.array([[3, 4], [5, 6]])
105+
assert data.dtype == np.int64
106+
m = folium.Map()
107+
HeatMap(data).add_to(m)
108+
assert "L.heatLayer" in m.get_root().render()

0 commit comments

Comments
 (0)