Skip to content

Commit 85ec923

Browse files
Fix TopoJson non-collection geometries (#2250)
* Fix TopoJson non-collection geometries * Make TopoJson geometry lookup robust and fix second crash site in Popup/Tooltip
1 parent 389aa8c commit 85ec923

2 files changed

Lines changed: 80 additions & 13 deletions

File tree

folium/features.py

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1047,19 +1047,22 @@ def __init__(
10471047
def style_data(self) -> None:
10481048
"""Applies self.style_function to each feature of self.data."""
10491049

1050+
for feature in self._get_geometries():
1051+
feature.setdefault("properties", {}).setdefault("style", {}).update(
1052+
self.style_function(feature)
1053+
) # noqa
1054+
1055+
def _get_geometries(self) -> list:
1056+
"""Return the selected TopoJSON object as a list of geometries."""
1057+
10501058
def recursive_get(data, keys):
10511059
if len(keys):
10521060
return recursive_get(data.get(keys[0]), keys[1:])
10531061
else:
10541062
return data
10551063

1056-
geometries = recursive_get(self.data, self.object_path.split("."))[
1057-
"geometries"
1058-
] # noqa
1059-
for feature in geometries:
1060-
feature.setdefault("properties", {}).setdefault("style", {}).update(
1061-
self.style_function(feature)
1062-
) # noqa
1064+
geometry = recursive_get(self.data, self.object_path.split("."))
1065+
return geometry["geometries"] if "geometries" in geometry else [geometry]
10631066

10641067
def render(self, **kwargs):
10651068
"""Renders the HTML representation of the element."""
@@ -1200,12 +1203,7 @@ def render(self, **kwargs):
12001203
)
12011204
self.warn_for_geometry_collections()
12021205
elif isinstance(self._parent, TopoJson):
1203-
obj_name = self._parent.object_path.split(".")[-1]
1204-
keys = tuple(
1205-
self._parent.data["objects"][obj_name]["geometries"][0][
1206-
"properties"
1207-
].keys()
1208-
)
1206+
keys = tuple(self._parent._get_geometries()[0]["properties"].keys())
12091207
else:
12101208
raise TypeError(
12111209
f"You cannot add a {self._name} to anything other than a "

tests/test_features.py

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,75 @@
1717
from folium.utilities import JsCode
1818

1919

20+
@pytest.mark.parametrize("geometry_type", ["Polygon", "MultiPolygon"])
21+
def test_topojson_non_collection_geometry(geometry_type):
22+
"""TopoJson supports objects that are not GeometryCollections."""
23+
style = {"color": "red"}
24+
topology = {
25+
"type": "Topology",
26+
"objects": {
27+
"shape": {
28+
"type": geometry_type,
29+
"arcs": [[0]] if geometry_type == "Polygon" else [[[0]]],
30+
}
31+
},
32+
"arcs": [[[0, 0], [1, 0], [0, 1], [-1, -1]]],
33+
"transform": {"scale": [1, 1], "translate": [0, 0]},
34+
}
35+
36+
map_ = folium.Map()
37+
folium.TopoJson(
38+
topology, "objects.shape", style_function=lambda feature: style
39+
).add_to(map_)
40+
41+
map_.get_root().render()
42+
43+
assert topology["objects"]["shape"]["properties"]["style"] == style
44+
45+
46+
def test_topojson_non_collection_geometry_without_type():
47+
"""TopoJson does not require a type key to apply styles."""
48+
topology = {
49+
"type": "Topology",
50+
"objects": {"shape": {"arcs": [[0]]}},
51+
"arcs": [[[0, 0], [1, 0], [0, 1], [-1, -1]]],
52+
"transform": {"scale": [1, 1], "translate": [0, 0]},
53+
}
54+
55+
topojson = folium.TopoJson(
56+
topology,
57+
"objects.shape",
58+
style_function=lambda feature: {"color": "red"},
59+
)
60+
61+
topojson.style_data()
62+
63+
assert topology["objects"]["shape"]["properties"]["style"] == {"color": "red"}
64+
65+
66+
@pytest.mark.parametrize("detail_type", [folium.GeoJsonPopup, folium.GeoJsonTooltip])
67+
def test_topojson_non_collection_geometry_detail(detail_type):
68+
"""TopoJson popup and tooltip fields support non-collection geometries."""
69+
topology = {
70+
"type": "Topology",
71+
"objects": {
72+
"shape": {
73+
"type": "Polygon",
74+
"arcs": [[0]],
75+
"properties": {"name": "A"},
76+
}
77+
},
78+
"arcs": [[[0, 0], [1, 0], [0, 1], [-1, -1]]],
79+
"transform": {"scale": [1, 1], "translate": [0, 0]},
80+
}
81+
82+
map_ = folium.Map()
83+
topojson = folium.TopoJson(topology, "objects.shape").add_to(map_)
84+
detail_type(fields=["name"]).add_to(topojson)
85+
86+
assert "name" in map_.get_root().render()
87+
88+
2089
@pytest.fixture
2190
def tmpl():
2291
yield ("""

0 commit comments

Comments
 (0)