Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
python-visualization
GitHub Repository: python-visualization/folium
Path: blob/main/tests/test_utilities.py
1593 views
1
import numpy as np
2
import pandas as pd
3
import pytest
4
5
from folium import FeatureGroup, Map, Marker, Popup
6
from folium.utilities import (
7
JsCode,
8
_is_url,
9
camelize,
10
deep_copy,
11
escape_double_quotes,
12
get_obj_in_upper_tree,
13
if_pandas_df_convert_to_numpy,
14
javascript_identifier_path_to_array_notation,
15
normalize_bounds_type,
16
parse_font_size,
17
parse_options,
18
validate_location,
19
validate_locations,
20
validate_multi_locations,
21
)
22
23
24
@pytest.mark.parametrize(
25
"location",
26
[
27
(5, 3),
28
[5.0, 3.0],
29
np.array([5, 3]),
30
np.array([[5, 3]]),
31
pd.Series([5, 3]),
32
pd.DataFrame([5, 3]),
33
pd.DataFrame([[5, 3]]),
34
("5.0", "3.0"),
35
("5", "3"),
36
],
37
)
38
def test_validate_location(location):
39
outcome = validate_location(location)
40
assert outcome == [5.0, 3.0]
41
42
43
@pytest.mark.parametrize(
44
"location",
45
[
46
None,
47
[None, None],
48
(),
49
[0],
50
["hi"],
51
"hi",
52
("lat", "lon"),
53
Marker,
54
(Marker, Marker),
55
(3.0, np.nan),
56
{3.0, 5.0},
57
{"lat": 5.0, "lon": 3.0},
58
range(4),
59
[0, 1, 2],
60
[(0,), (1,)],
61
],
62
)
63
def test_validate_location_exceptions(location):
64
"""Test input that should raise an exception."""
65
with pytest.raises((TypeError, ValueError)):
66
validate_location(location)
67
68
69
@pytest.mark.parametrize(
70
"locations",
71
[
72
[(0, 5), (1, 6), (2, 7)],
73
[[0, 5], [1, 6], [2, 7]],
74
np.array([[0, 5], [1, 6], [2, 7]]),
75
pd.DataFrame([[0, 5], [1, 6], [2, 7]]),
76
],
77
)
78
def test_validate_locations(locations):
79
outcome = validate_locations(locations)
80
assert outcome == [[0.0, 5.0], [1.0, 6.0], [2.0, 7.0]]
81
82
83
@pytest.mark.parametrize(
84
"locations",
85
[
86
[[(0, 5), (1, 6), (2, 7)], [(3, 8), (4, 9)]],
87
],
88
)
89
def test_validate_multi_locations(locations):
90
outcome = validate_multi_locations(locations)
91
assert outcome == [[[0, 5], [1, 6], [2, 7]], [[3, 8], [4, 9]]]
92
93
94
@pytest.mark.parametrize(
95
"locations",
96
[
97
None,
98
[None, None],
99
(),
100
[0],
101
["hi"],
102
"hi",
103
("lat", "lon"),
104
Marker,
105
(Marker, Marker),
106
(3.0, np.nan),
107
{3.0, 5.0},
108
{"lat": 5.0, "lon": 3.0},
109
range(4),
110
[0, 1, 2],
111
[(0,), (1,)],
112
],
113
)
114
def test_validate_locations_exceptions(locations):
115
"""Test input that should raise an exception."""
116
with pytest.raises((TypeError, ValueError)):
117
validate_locations(locations)
118
119
120
def test_if_pandas_df_convert_to_numpy():
121
data = [[0, 5, "red"], [1, 6, "blue"], [2, 7, "something"]]
122
df = pd.DataFrame(data, columns=["lat", "lng", "color"])
123
res = if_pandas_df_convert_to_numpy(df)
124
assert isinstance(res, np.ndarray)
125
expected = np.array(data)
126
assert all(
127
[
128
[all([i == j]) for i, j in zip(row1, row2)]
129
for row1, row2 in zip(res, expected)
130
]
131
)
132
# Also check if it ignores things that are not Pandas DataFrame:
133
assert if_pandas_df_convert_to_numpy(data) is data
134
assert if_pandas_df_convert_to_numpy(expected) is expected
135
136
137
@pytest.mark.parametrize(
138
"bounds, expected",
139
[
140
([[1, 2], [3, 4]], [[1.0, 2.0], [3.0, 4.0]]),
141
([[None, 2], [3, None]], [[None, 2.0], [3.0, None]]),
142
([[1.1, 2.2], [3.3, 4.4]], [[1.1, 2.2], [3.3, 4.4]]),
143
([[None, None], [None, None]], [[None, None], [None, None]]),
144
([[0, -1], [-2, 3]], [[0.0, -1.0], [-2.0, 3.0]]),
145
],
146
)
147
def test_normalize_bounds_type(bounds, expected):
148
assert normalize_bounds_type(bounds) == expected
149
150
151
def test_camelize():
152
assert camelize("variable_name") == "variableName"
153
assert camelize("variableName") == "variableName"
154
assert camelize("name") == "name"
155
assert camelize("very_long_variable_name") == "veryLongVariableName"
156
157
158
def test_deep_copy():
159
m = Map()
160
fg = FeatureGroup().add_to(m)
161
Marker(location=(0, 0)).add_to(fg)
162
m_copy = deep_copy(m)
163
164
def check(item, item_copy):
165
assert type(item) is type(item_copy)
166
assert item._name == item_copy._name
167
for attr in item.__dict__.keys():
168
if not attr.startswith("_"):
169
assert getattr(item, attr) == getattr(item_copy, attr)
170
assert item is not item_copy
171
assert item._id != item_copy._id
172
for child, child_copy in zip(
173
item._children.values(), item_copy._children.values()
174
):
175
check(child, child_copy)
176
177
check(m, m_copy)
178
179
180
def test_get_obj_in_upper_tree():
181
m = Map()
182
fg = FeatureGroup().add_to(m)
183
marker = Marker(location=(0, 0)).add_to(fg)
184
assert get_obj_in_upper_tree(marker, FeatureGroup) is fg
185
assert get_obj_in_upper_tree(marker, Map) is m
186
# The search should only go up, not down:
187
with pytest.raises(ValueError):
188
assert get_obj_in_upper_tree(fg, Marker)
189
with pytest.raises(ValueError):
190
assert get_obj_in_upper_tree(marker, Popup)
191
192
193
def test_parse_options():
194
assert parse_options(thing=42) == {"thing": 42}
195
assert parse_options(thing=None) == {}
196
assert parse_options(long_thing=42) == {"longThing": 42}
197
assert parse_options(thing=42, lst=[1, 2]) == {"thing": 42, "lst": [1, 2]}
198
199
200
@pytest.mark.parametrize(
201
"url",
202
[
203
"https://example.com/img.png",
204
"http://example.com/img.png",
205
"ftp://example.com/img.png",
206
"file:///t.jpg",
207
"data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7",
208
],
209
)
210
def test_is_url(url):
211
assert _is_url(url) is True
212
213
214
@pytest.mark.parametrize(
215
"text,result",
216
[
217
("bla", "bla"),
218
('bla"bla', r"bla\"bla"),
219
('"bla"bla"', r"\"bla\"bla\""),
220
],
221
)
222
def test_escape_double_quotes(text, result):
223
assert escape_double_quotes(text) == result
224
225
226
@pytest.mark.parametrize(
227
"text,result",
228
[
229
("bla", '["bla"]'),
230
("obj-1.obj2", '["obj-1"]["obj2"]'),
231
('obj-1.obj"2', r'["obj-1"]["obj\"2"]'),
232
],
233
)
234
def test_javascript_identifier_path_to_array_notation(text, result):
235
assert javascript_identifier_path_to_array_notation(text) == result
236
237
238
def test_js_code_init_str():
239
js_code = JsCode("hi")
240
assert isinstance(js_code, JsCode)
241
assert isinstance(js_code.js_code, str)
242
243
244
def test_js_code_init_js_code():
245
js_code = JsCode("hi")
246
js_code_2 = JsCode(js_code)
247
assert isinstance(js_code_2, JsCode)
248
assert isinstance(js_code_2.js_code, str)
249
250
251
@pytest.mark.parametrize(
252
"value,expected",
253
[
254
(10, "10px"),
255
(12.5, "12.5px"),
256
("1rem", "1rem"),
257
("1em", "1em"),
258
],
259
)
260
def test_parse_font_size_valid(value, expected):
261
assert parse_font_size(value) == expected
262
263
264
invalid_values = ["1", "1unit"]
265
expected_errors = "The font size must be expressed in rem, em, or px."
266
267
268
@pytest.mark.parametrize("value,error_message", zip(invalid_values, expected_errors))
269
def test_parse_font_size_invalid(value, error_message):
270
with pytest.raises(ValueError, match=error_message):
271
parse_font_size(value)
272
273